Vendor import of OpenBSD's pf userland as of OpenBSD 3.4

Approved by: bms(mentor), core(in general)
This commit is contained in:
mlaier 2004-02-28 16:52:45 +00:00
commit 2135f6a83c
27 changed files with 20141 additions and 0 deletions

463
contrib/pf/authpf/authpf.8 Normal file
View File

@ -0,0 +1,463 @@
.\" $OpenBSD: authpf.8,v 1.30 2003/08/17 23:24:47 henning Exp $
.\"
.\" Copyright (c) 2002 Bob Beck (beck@openbsd.org>. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd January 10, 2002
.Dt AUTHPF 8
.Os
.Sh NAME
.Nm authpf
.Nd authenticating gateway user shell
.Sh SYNOPSIS
.Nm authpf
.Sh DESCRIPTION
.Nm
is a user shell for authenticating gateways.
It is used to change
.Xr pf 4
rules when a user authenticates and starts a session with
.Xr sshd 8
and to undo these changes when the user's session exits.
It is designed for changing filter and translation rules for an individual
source IP address as long as a user maintains an active
.Xr ssh 1
session.
Typical use would be for a gateway that authenticates users before
allowing them Internet use, or a gateway that allows different users into
different places.
.Nm
logs the successful start and end of a session to
.Xr syslogd 8 .
This, combined with properly set up filter rules and secure switches,
can be used to ensure users are held accountable for their network traffic.
.Pp
.Nm
can add filter and translation rules using the syntax described in
.Xr pf.conf 5 .
.Nm
requires that the
.Xr pf 4
system be enabled before use.
.Pp
.Nm
is meant to be used with users who can connect via
.Xr ssh 1
only.
On startup,
.Nm
retrieves the client's connecting IP address via the
.Ev SSH_CLIENT
environment variable and, after performing additional access checks,
reads a template file to determine what filter and translation rules
(if any) to add.
On session exit the same rules that were added at startup are removed.
.Pp
Each
.Nm
process stores its rules in a separate ruleset inside a
.Xr pf 4
.Pa anchor
shared by all
.Nm
processes.
By default, the
.Pa anchor
name "authpf" is used, and the ruleset names equal the PIDs of the
.Nm
processes.
The following rules need to be added to the main ruleset
.Pa /etc/pf.conf
in order to cause evaluation of any
.Nm
rules:
.Bd -literal -offset indent
nat-anchor authpf
rdr-anchor authpf
binat-anchor authpf
anchor authpf
.Ed
.Sh FILTER AND TRANSLATION RULES
Filter and translation rules for
.Nm
use the same format described in
.Xr pf.conf 5 .
The only difference is that these rules may (and probably should) use
the macro
.Em user_ip ,
which is assigned the connecting IP address whenever
.Nm
is run.
Additionally, the macro
.Em user_id
is assigned the user name.
.Pp
Filter and nat rules will first be searched for in
.Pa /etc/authpf/users/$USER/
and then in
.Pa /etc/authpf/ .
Per-user rules from the
.Pa /etc/authpf/users/$USER/
directory are intended to be used when non-default rules
are needed on an individual user basis.
It is important to ensure that a user can not write or change
these configuration files.
.Pp
Filter and translation rules are loaded from the file
.Pa /etc/authpf/users/$USER/authpf.rules .
If this file does not exist the file
.Pa /etc/authpf/authpf.rules
is used.
The
.Pa authpf.rules
file must exist in one of the above locations for
.Nm
to run.
.Pp
Translation rules are also loaded from this file.
The use of translation rules in an
.Pa authpf.rules
file is optional.
.Sh CONFIGURATION
Options are controlled by the
.Pa /etc/authpf/authpf.conf
file.
If the file is empty, defaults are used for all
configuration options.
The file consists of pairs of the form
.Li name=value ,
one per line.
Currently, the allowed values are as follows:
.Bl -tag -width Ds
.It anchor=name
Use the specified
.Pa anchor
name instead of "authpf".
.El
.Sh USER MESSAGES
On successful invocation,
.Nm
displays a message telling the user he or she has been authenticated.
It will additionally display the contents of the file
.Pa /etc/authpf/authpf.message
if the file exists and is readable.
.Pp
There exist two methods for providing additional granularity to the control
offered by
.Nm
- it is possible to set the gateway to explicitly allow users who have
authenticated to
.Xr ssh 1
and deny access to only a few troublesome individuals.
This is done by creating a file with the banned user's login name as the
filename in
.Pa /etc/authpf/banned/ .
The contents of this file will be displayed to a banned user, thus providing
a method for informing the user that they have been banned, and where they can
go and how to get there if they want to have their service restored.
This is the default behaviour.
.Pp
It is also possible to configure
.Nm
to only allow specific users access.
This is done by listing their login names, one per line, in
.Pa /etc/authpf/authpf.allow .
If "*" is found on a line, then all usernames match.
If
.Nm
is unable to verify the user's permission to use the gateway, it will
print a brief message and die.
It should be noted that a ban takes precedence over an allow.
.Pp
On failure, messages will be logged to
.Xr syslogd 8
for the system administrator.
The user does not see these, but will be told the system is unavailable due to
technical difficulties.
The contents of the file
.Pa /etc/authpf/authpf.problem
will also be displayed if the file exists and is readable.
.Sh CONFIGURATION ISSUES
.Nm
maintains the changed filter rules as long as the user maintains an
active session.
It is important to remember however, that the existence
of this session means the user is authenticated.
Because of this, it is important to configure
.Xr sshd 8
to ensure the security of the session, and to ensure that the network
through which users connect is secure.
.Xr sshd 8
should be configured to use the
.Ar ClientAliveInterval
and
.Ar ClientAliveCountMax
parameters to ensure that a ssh session is terminated quickly if
it becomes unresponsive, or if arp or address spoofing is used to
hijack the session.
Note that TCP keepalives are not sufficient for
this, since they are not secure.
.Pp
.Nm
will remove statetable entries that were created during a user's
session.
This ensures that there will be no unauthenticated traffic
allowed to pass after the controlling
.Xr ssh 1
session has been closed.
.Pp
.Nm
is designed for gateway machines which typically do not have regular
(non-administrative) users using the machine.
An administrator must remember that
.Nm
can be used to modify the filter rules through the environment in
which it is run, and as such could be used to modify the filter rules
(based on the contents of the configuration files) by regular
users.
In the case where a machine has regular users using it, as well
as users with
.Nm
as their shell, the regular users should be prevented from running
.Nm
by using the
.Pa /etc/authpf/authpf.allow
or
.Pa /etc/authpf/banned/
facilities.
.Pp
.Nm
modifies the packet filter and address translation rules, and because
of this it needs to be configured carefully.
.Nm
will not run and will exit silently if the
.Pa /etc/authpf/authpf.conf
file does not exist.
After considering the effect
.Nm
may have on the main packet filter rules, the system administrator may
enable
.Nm
by creating an appropriate
.Pa /etc/authpf/authpf.conf
file.
.Sh EXAMPLES
\fBControl Files\fP - To illustrate the user-specific access control
mechanisms, let us consider a typical user named bob.
Normally, as long as bob can authenticate himself, the
.Nm
program will load the appropriate rules.
Enter the
.Pa /etc/authpf/banned/
directory.
If bob has somehow fallen from grace in the eyes of the
powers-that-be, they can prohibit him from using the gateway by creating
the file
.Pa /etc/authpf/banned/bob
containing a message about why he has been banned from using the network.
Once bob has done suitable penance, his access may be restored by moving or
removing the file
.Pa /etc/authpf/banned/bob .
.Pp
Now consider a workgroup containing alice, bob, carol and dave.
They have a
wireless network which they would like to protect from unauthorized use.
To accomplish this, they create the file
.Pa /etc/authpf/authpf.allow
which lists their login ids, one per line.
At this point, even if eve could authenticate to
.Xr sshd 8 ,
she would not be allowed to use the gateway.
Adding and removing users from
the work group is a simple matter of maintaining a list of allowed userids.
If bob once again manages to annoy the powers-that-be, they can ban him from
using the gateway by creating the familiar
.Pa /etc/authpf/banned/bob
file.
Though bob is listed in the allow file, he is prevented from using
this gateway due to the existence of a ban file.
.Pp
\fBDistributed Authentication\fP - It is often desirable to interface with a
distributed password system rather than forcing the sysadmins to keep a large
number of local password files in sync.
The
.Xr login.conf 5
mechanism in
.Ox
can be used to fork the right shell.
To make that happen,
.Xr login.conf 5
should have entries that look something like this:
.Bd -literal -offset indent
shell-default:shell=/bin/csh
default:\e
...
:shell=/usr/sbin/authpf
daemon:\e
...
:shell=/bin/csh:\e
:tc=default:
staff:\e
...
:shell=/bin/csh:\e
:tc=default:
.Ed
.Pp
Using a default password file, all users will get
.Nm
as their shell except for root who will get
.Pa /bin/csh .
.Pp
\fBSSH Configuration\fP - As stated earlier,
.Xr sshd 8
must be properly configured to detect and defeat network attacks.
To that end, the following options should be added to
.Xr sshd_config 5 :
.Bd -literal -offset indent
Protocol 2
ClientAliveInterval 15
ClientAliveCountMax 3
.Ed
.Pp
This ensures that unresponsive or spoofed sessions are terminated within a
minute, since a hijacker should not be able to spoof ssh keepalive messages.
.Pp
\fBBanners\fP - Once authenticated, the user is shown the contents of
.Pa /etc/authpf/authpf.message .
This message may be a screen-full of the appropriate use policy, the contents
of
.Pa /etc/motd
or something as simple as the following:
.Bd -literal -offset indent
This means you will be held accountable by the powers that be
for traffic originating from your machine, so please play nice.
.Ed
.Pp
To tell the user where to go when the system is broken,
.Pa /etc/authpf/authpf.problem
could contain something like this:
.Bd -literal -offset indent
Sorry, there appears to be some system problem. To report this
problem so we can fix it, please phone 1-900-314-1597 or send
an email to remove@bulkmailerz.net.
.Ed
.Pp
\fBPacket Filter Rules\fP - In areas where this gateway is used to protect a
wireless network (a hub with several hundred ports), the default rule set as
well as the per-user rules should probably allow very few things beyond
encrypted protocols like
.Xr ssh 1 ,
.Xr ssl 8 ,
or
.Xr ipsec 4 .
On a securely switched network, with plug-in jacks for visitors who are
given authentication accounts, you might want to allow out everything.
In this context, a secure switch is one that tries to prevent address table
overflow attacks.
The examples below assume a switched wired net.
.Pp
Example
.Pa /etc/pf.conf :
.Bd -literal
# by default we allow internal clients to talk to us using
# ssh and use us as a dns server.
internal_if=\&"fxp1\&"
gateway_addr=\&"10.0.1.1\&"
nat-anchor authpf
rdr-anchor authpf
binat-anchor authpf
block in on $internal_if from any to any
pass in quick on $internal_if proto tcp from any to $gateway_addr \e
port = ssh
pass in quick on $internal_if proto udp from any to $gateway_addr \e
port = domain
anchor authpf
.Ed
.Pp
Example
.Pa /etc/authpf/authpf.rules :
.Bd -literal
# no real restrictions here, basically turn the network jack off or on.
external_if = \&"xl0\&"
internal_if = \&"fxp0\&"
pass in log quick on $internal_if proto tcp from $user_ip to any \e
keep state
pass in quick on $internal_if from $user_ip to any
.Ed
.Pp
Another example
.Pa /etc/authpf/authpf.rules
for an insecure network (such as a public wireless network) where
we might need to be a bit more restrictive.
.Bd -literal
internal_if=\&"fxp1\&"
ipsec_gw=\&"10.2.3.4\&"
# rdr ftp for proxying by ftp-proxy(8)
rdr on $internal_if proto tcp from $user_ip to any port 21 \e
-> 127.0.0.1 port 8081
# allow out ftp, ssh, www and https only, and allow user to negotiate
# ipsec with the ipsec server.
pass in log quick on $internal_if proto tcp from $user_ip to any \e
port { 21, 22, 80, 443 } flags S/SA
pass in quick on $internal_if proto tcp from $user_ip to any \e
port { 21, 22, 80, 443 }
pass in quick proto udp from $user_ip to $ipsec_gw port = isakmp \e
keep state
pass in quick proto esp from $user_ip to $ipsec_gw
.Ed
.Sh FILES
.Bl -tag -width "/etc/authpf/authpf.conf" -compact
.It Pa /etc/authpf/authpf.conf
.It Pa /etc/authpf/authpf.allow
.It Pa /etc/authpf/authpf.rules
.It Pa /etc/authpf/authpf.message
.It Pa /etc/authpf/authpf.problem
.El
.Sh SEE ALSO
.Xr pf 4 ,
.Xr pf.conf 5 ,
.Xr ftp-proxy 8
.Sh HISTORY
The
.Nm
program first appeared in
.Ox 3.1 .
.Sh BUGS
Configuration issues are tricky.
The authenticating
.Xr ssh 1
connection may be secured, but if the network is not secured the user may
expose insecure protocols to attackers on the same network, or enable other
attackers on the network to pretend to be the user by spoofing their IP
address.
.Pp
.Nm
is not designed to prevent users from denying service to other users.

871
contrib/pf/authpf/authpf.c Normal file
View File

@ -0,0 +1,871 @@
/* $OpenBSD: authpf.c,v 1.68 2003/08/21 19:13:23 frantzen Exp $ */
/*
* Copyright (C) 1998 - 2002 Bob Beck (beck@openbsd.org).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <pfctl_parser.h>
#include "pathnames.h"
extern int symset(const char *, const char *, int);
static int read_config(FILE *);
static void print_message(char *);
static int allowed_luser(char *);
static int check_luser(char *, char *);
static int remove_stale_rulesets(void);
static int change_filter(int, const char *, const char *);
static void authpf_kill_states(void);
int dev; /* pf device */
char anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
char rulesetname[PF_RULESET_NAME_SIZE];
FILE *pidfp;
char *infile; /* file name printed by yyerror() in parse.y */
char luser[MAXLOGNAME]; /* username */
char ipsrc[256]; /* ip as a string */
char pidfile[MAXPATHLEN]; /* we save pid in this file. */
struct timeval Tstart, Tend; /* start and end times of session */
volatile sig_atomic_t want_death;
static void need_death(int signo);
static __dead void do_death(int);
/*
* User shell for authenticating gateways. Sole purpose is to allow
* a user to ssh to a gateway, and have the gateway modify packet
* filters to allow access, then remove access when the user finishes
* up. Meant to be used only from ssh(1) connections.
*/
int
main(int argc, char *argv[])
{
int lockcnt = 0, n, pidfd;
FILE *config;
struct in_addr ina;
struct passwd *pw;
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) {
syslog(LOG_ERR, "non-interactive session connection for authpf");
exit(1);
}
if ((cp = getenv("SSH_CLIENT")) == NULL) {
syslog(LOG_ERR, "cannot determine connection source");
exit(1);
}
if (strlcpy(ipsrc, cp, sizeof(ipsrc)) >= sizeof(ipsrc)) {
syslog(LOG_ERR, "SSH_CLIENT variable too long");
exit(1);
}
cp = strchr(ipsrc, ' ');
if (!cp) {
syslog(LOG_ERR, "corrupt SSH_CLIENT variable %s", ipsrc);
exit(1);
}
*cp = '\0';
if (inet_pton(AF_INET, ipsrc, &ina) != 1) {
syslog(LOG_ERR,
"cannot determine IP from SSH_CLIENT %s", ipsrc);
exit(1);
}
/* open the pf device */
dev = open(PATH_DEVFILE, O_RDWR);
if (dev == -1) {
syslog(LOG_ERR, "cannot open packet filter device (%m)");
goto die;
}
uid = getuid();
pw = getpwuid(uid);
if (pw == NULL) {
syslog(LOG_ERR, "cannot find user for uid %u", uid);
goto die;
}
if (strcmp(pw->pw_shell, PATH_AUTHPF_SHELL)) {
syslog(LOG_ERR, "wrong shell for user %s, uid %u",
pw->pw_name, pw->pw_uid);
goto die;
}
/*
* Paranoia, but this data _does_ come from outside authpf, and
* truncation would be bad.
*/
if (strlcpy(luser, pw->pw_name, sizeof(luser)) >= sizeof(luser)) {
syslog(LOG_ERR, "username too long: %s", pw->pw_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)) {
syslog(LOG_ERR, "path to pidfile too long");
goto die;
}
/*
* If someone else is already using this ip, then this person
* wants to switch users - so kill the old process and exit
* as well.
*
* Note, we could print a message and tell them to log out, but the
* usual case of this is that someone has left themselves logged in,
* with the authenticated connection iconized and someone else walks
* up to use and automatically logs in before using. If this just
* gets rid of the old one silently, the new user never knows they
* could have used someone else's old authentication. If we
* tell them to log out before switching users it is an invitation
* for abuse.
*/
do {
int save_errno, otherpid = -1;
char otherluser[MAXLOGNAME];
if ((pidfd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 ||
(pidfp = fdopen(pidfd, "r+")) == NULL) {
if (pidfd != -1)
close(pidfd);
syslog(LOG_ERR, "cannot open or create %s: %s", pidfile,
strerror(errno));
goto die;
}
if (flock(fileno(pidfp), LOCK_EX|LOCK_NB) == 0)
break;
save_errno = errno;
/* Mark our pid, and username to our file. */
rewind(pidfp);
/* 31 == MAXLOGNAME - 1 */
if (fscanf(pidfp, "%d\n%31s\n", &otherpid, otherluser) != 2)
otherpid = -1;
syslog(LOG_DEBUG, "tried to lock %s, in use by pid %d: %s",
pidfile, otherpid, strerror(save_errno));
if (otherpid > 0) {
syslog(LOG_INFO,
"killing prior auth (pid %d) of %s by user %s",
otherpid, ipsrc, otherluser);
if (kill((pid_t) otherpid, SIGTERM) == -1) {
syslog(LOG_INFO,
"could not kill process %d: (%m)",
otherpid);
}
}
/*
* we try to kill the previous process and acquire the lock
* for 10 seconds, trying once a second. if we can't after
* 10 attempts we log an error and give up
*/
if (++lockcnt > 10) {
syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
otherpid);
goto dogdeath;
}
sleep(1);
/* re-open, and try again. The previous authpf process
* we killed above should unlink the file and release
* it's lock, giving us a chance to get it now
*/
fclose(pidfp);
} while (1);
/* revoke privs */
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())
do_death(0);
/* We appear to be making headway, so actually mark our pid */
rewind(pidfp);
fprintf(pidfp, "%ld\n%s\n", (long)getpid(), luser);
fflush(pidfp);
(void) ftruncate(fileno(pidfp), ftell(pidfp));
if (change_filter(1, luser, ipsrc) == -1) {
printf("Unable to modify filters\r\n");
do_death(1);
}
signal(SIGTERM, need_death);
signal(SIGINT, need_death);
signal(SIGALRM, need_death);
signal(SIGPIPE, need_death);
signal(SIGHUP, need_death);
signal(SIGSTOP, need_death);
signal(SIGTSTP, need_death);
while (1) {
printf("\r\nHello %s, ", luser);
printf("You are authenticated from host \"%s\"\r\n", ipsrc);
setproctitle("%s@%s", luser, ipsrc);
print_message(PATH_MESSAGE);
while (1) {
sleep(10);
if (want_death)
do_death(1);
}
}
/* NOTREACHED */
dogdeath:
printf("\r\n\r\nSorry, this service is currently unavailable due to ");
printf("technical difficulties\r\n\r\n");
print_message(PATH_PROBLEM);
printf("\r\nYour authentication process (pid %ld) was unable to run\n",
(long)getpid());
sleep(180); /* them lusers read reaaaaal slow */
die:
do_death(0);
}
/*
* reads config file in PATH_CONFFILE to set optional behaviours up
*/
static int
read_config(FILE *f)
{
char buf[1024];
int i = 0;
do {
char **ap;
char *pair[4], *cp, *tp;
int len;
if (fgets(buf, sizeof(buf), f) == NULL) {
fclose(f);
return (0);
}
i++;
len = strlen(buf);
if (buf[len - 1] != '\n' && !feof(f)) {
syslog(LOG_ERR, "line %d too long in %s", i,
PATH_CONFFILE);
return (1);
}
buf[len - 1] = '\0';
for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
; /* nothing */
if (!*cp || *cp == '#' || *cp == '\n')
continue;
for (ap = pair; ap < &pair[3] &&
(*ap = strsep(&cp, "=")) != NULL; ) {
if (**ap != '\0')
ap++;
}
if (ap != &pair[2])
goto parse_error;
tp = pair[1] + strlen(pair[1]);
while ((*tp == ' ' || *tp == '\t') && tp >= pair[1])
*tp-- = '\0';
if (strcasecmp(pair[0], "anchor") == 0) {
if (!pair[1][0] || strlcpy(anchorname, pair[1],
sizeof(anchorname)) >= sizeof(anchorname))
goto parse_error;
}
} while (!feof(f) && !ferror(f));
fclose(f);
return (0);
parse_error:
fclose(f);
syslog(LOG_ERR, "parse error, line %d of %s", i, PATH_CONFFILE);
return (1);
}
/*
* splatter a file to stdout - max line length of 1024,
* used for spitting message files at users to tell them
* they've been bad or we're unavailable.
*/
static void
print_message(char *filename)
{
char buf[1024];
FILE *f;
if ((f = fopen(filename, "r")) == NULL)
return; /* fail silently, we don't care if it isn't there */
do {
if (fgets(buf, sizeof(buf), f) == NULL) {
fflush(stdout);
fclose(f);
return;
}
} while (fputs(buf, stdout) != EOF && !feof(f));
fflush(stdout);
fclose(f);
}
/*
* allowed_luser checks to see if user "luser" is allowed to
* use this gateway by virtue of being listed in an allowed
* users file, namely /etc/authpf/authpf.allow .
*
* If /etc/authpf/authpf.allow does not exist, then we assume that
* all users who are allowed in by sshd(8) are permitted to
* use this gateway. If /etc/authpf/authpf.allow does exist, then a
* user must be listed if the connection is to continue, else
* the session terminates in the same manner as being banned.
*/
static int
allowed_luser(char *luser)
{
char *buf, *lbuf;
int matched;
size_t len;
FILE *f;
if ((f = fopen(PATH_ALLOWFILE, "r")) == NULL) {
if (errno == ENOENT) {
/*
* allowfile doesn't exist, thus this gateway
* isn't restricted to certain users...
*/
return (1);
}
/*
* luser may in fact be allowed, but we can't open
* the file even though it's there. probably a config
* problem.
*/
syslog(LOG_ERR, "cannot open allowed users file %s (%s)",
PATH_ALLOWFILE, strerror(errno));
return (0);
} else {
/*
* /etc/authpf/authpf.allow exists, thus we do a linear
* search to see if they are allowed.
* also, if username "*" exists, then this is a
* "public" gateway, such as it is, so let
* everyone use it.
*/
lbuf = NULL;
while ((buf = fgetln(f, &len))) {
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
else {
if ((lbuf = (char *)malloc(len + 1)) == NULL)
err(1, NULL);
memcpy(lbuf, buf, len);
lbuf[len] = '\0';
buf = lbuf;
}
matched = strcmp(luser, buf) == 0 || strcmp("*", buf) == 0;
if (lbuf != NULL) {
free(lbuf);
lbuf = NULL;
}
if (matched)
return (1); /* matched an allowed username */
}
syslog(LOG_INFO, "denied access to %s: not listed in %s",
luser, PATH_ALLOWFILE);
/* reuse buf */
buf = "\n\nSorry, you are not allowed to use this facility!\n";
fputs(buf, stdout);
}
fflush(stdout);
return (0);
}
/*
* check_luser checks to see if user "luser" has been banned
* from using us by virtue of having an file of the same name
* in the "luserdir" directory.
*
* If the user has been banned, we copy the contents of the file
* to the user's screen. (useful for telling the user what to
* do to get un-banned, or just to tell them they aren't
* going to be un-banned.)
*/
static int
check_luser(char *luserdir, char *luser)
{
FILE *f;
int n;
char tmp[MAXPATHLEN];
n = snprintf(tmp, sizeof(tmp), "%s/%s", luserdir, luser);
if (n < 0 || (u_int)n >= sizeof(tmp)) {
syslog(LOG_ERR, "provided banned directory line too long (%s)",
luserdir);
return (0);
}
if ((f = fopen(tmp, "r")) == NULL) {
if (errno == ENOENT) {
/*
* file or dir doesn't exist, so therefore
* this luser isn't banned.. all is well
*/
return (1);
} else {
/*
* luser may in fact be banned, but we can't open the
* file even though it's there. probably a config
* problem.
*/
syslog(LOG_ERR, "cannot open banned file %s (%s)",
tmp, strerror(errno));
return (0);
}
} else {
/*
* luser is banned - spit the file at them to
* tell what they can do and where they can go.
*/
syslog(LOG_INFO, "denied access to %s: %s exists",
luser, tmp);
/* reuse tmp */
strlcpy(tmp, "\n\n-**- Sorry, you have been banned! -**-\n\n",
sizeof(tmp));
while (fputs(tmp, stdout) != EOF && !feof(f)) {
if (fgets(tmp, sizeof(tmp), f) == NULL) {
fflush(stdout);
return (0);
}
}
}
fflush(stdout);
return (0);
}
/*
* Search for rulesets left by other authpf processes (either because they
* died ungracefully or were terminated) and remove them.
*/
static int
remove_stale_rulesets(void)
{
struct pfioc_ruleset prs;
const int action[PF_RULESET_MAX] = { PF_SCRUB,
PF_PASS, PF_NAT, PF_BINAT, PF_RDR };
u_int32_t nr, mnr;
memset(&prs, 0, sizeof(prs));
strlcpy(prs.anchor, anchorname, sizeof(prs.anchor));
if (ioctl(dev, DIOCGETRULESETS, &prs)) {
if (errno == EINVAL)
return (0);
else
return (1);
}
mnr = prs.nr;
nr = 0;
while (nr < mnr) {
char *s;
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)
return (1);
if (kill(pid, 0) && errno != EPERM) {
int i;
for (i = 0; i < PF_RULESET_MAX; ++i) {
struct pfioc_rule pr;
memset(&pr, 0, sizeof(pr));
memcpy(pr.anchor, prs.anchor, sizeof(pr.anchor));
memcpy(pr.ruleset, prs.name, sizeof(pr.ruleset));
pr.rule.action = action[i];
if ((ioctl(dev, DIOCBEGINRULES, &pr) ||
ioctl(dev, DIOCCOMMITRULES, &pr)) &&
errno != EINVAL)
return (1);
}
mnr--;
} else
nr++;
}
return (0);
}
/*
* Add/remove filter entries for user "luser" from ip "ipsrc"
*/
static int
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];
int i;
if (luser == NULL || !luser[0] || strlen(luser) >=
PF_RULESET_NAME_SIZE || ipsrc == NULL || !ipsrc[0]) {
syslog(LOG_ERR, "invalid luser/ipsrc");
goto error;
}
if (add) {
if ((i = snprintf(fn, sizeof(fn), "%s/%s/authpf.rules",
PATH_USER_DIR, luser)) < 0 || i >= sizeof(fn)) {
syslog(LOG_ERR, "user rule path too long");
goto error;
}
if ((f = fopen(fn, "r")) == NULL && errno != ENOENT) {
syslog(LOG_ERR, "cannot open %s (%m)", fn);
goto error;
}
if (f == NULL) {
if (strlcpy(fn, PATH_PFRULES, sizeof(fn)) >=
sizeof(fn)) {
syslog(LOG_ERR, "rule path too long");
goto error;
}
if ((f = fopen(fn, "r")) == NULL) {
syslog(LOG_ERR, "cannot open %s (%m)", fn);
goto error;
}
}
}
if (pfctl_load_fingerprints(dev, 0)) {
syslog(LOG_ERR, "unable to load kernel's OS fingerprints");
goto error;
}
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");
goto error;
}
pf.prule[i] = &pr[i];
}
if (add) {
if (symset("user_ip", ipsrc, 0) ||
symset("user_id", luser, 0)) {
syslog(LOG_ERR, "symset");
goto error;
}
pf.dev = dev;
infile = fn;
if (parse_rules(f, &pf) < 0) {
syslog(LOG_ERR, "syntax error in rule file: "
"authpf rules not loaded");
goto error;
}
infile = NULL;
fclose(f);
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 (add) {
gettimeofday(&Tstart, NULL);
syslog(LOG_INFO, "allowing %s, user %s", ipsrc, luser);
} else {
gettimeofday(&Tend, NULL);
syslog(LOG_INFO, "removed %s, user %s - duration %ld seconds",
ipsrc, luser, Tend.tv_sec - Tstart.tv_sec);
}
return (0);
error:
if (f != NULL)
fclose(f);
infile = NULL;
return (-1);
}
/*
* This is to kill off states that would otherwise be left behind stateful
* rules. This means we don't need to allow in more traffic than we really
* want to, since we don't have to worry about any luser sessions lasting
* longer than their ssh session. This function is based on
* pfctl_kill_states from pfctl.
*/
static void
authpf_kill_states(void)
{
struct pfioc_state_kill psk;
struct in_addr target;
memset(&psk, 0, sizeof(psk));
psk.psk_af = AF_INET;
inet_pton(AF_INET, ipsrc, &target);
/* Kill all states from ipsrc */
psk.psk_src.addr.v.a.addr.v4 = target;
memset(&psk.psk_src.addr.v.a.mask, 0xff,
sizeof(psk.psk_src.addr.v.a.mask));
if (ioctl(dev, DIOCKILLSTATES, &psk))
syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
/* Kill all states to ipsrc */
psk.psk_af = AF_INET;
memset(&psk.psk_src, 0, sizeof(psk.psk_src));
psk.psk_dst.addr.v.a.addr.v4 = target;
memset(&psk.psk_dst.addr.v.a.mask, 0xff,
sizeof(psk.psk_dst.addr.v.a.mask));
if (ioctl(dev, DIOCKILLSTATES, &psk))
syslog(LOG_ERR, "DIOCKILLSTATES failed (%m)");
}
/* signal handler that makes us go away properly */
static void
need_death(int signo)
{
want_death = 1;
}
/*
* function that removes our stuff when we go away.
*/
static __dead void
do_death(int active)
{
int ret = 0;
if (active) {
change_filter(0, luser, ipsrc);
authpf_kill_states();
remove_stale_rulesets();
}
if (pidfp)
ftruncate(fileno(pidfp), 0);
if (pidfile[0])
if (unlink(pidfile) == -1)
syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
exit(ret);
}
/*
* callbacks for parse_rules(void)
*/
int
pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)
{
struct pfioc_rule *pr;
switch (r->action) {
case PF_PASS:
case PF_DROP:
pr = pf->prule[PF_RULESET_FILTER];
break;
case PF_SCRUB:
pr = pf->prule[PF_RULESET_SCRUB];
break;
case PF_NAT:
case PF_NONAT:
pr = pf->prule[PF_RULESET_NAT];
break;
case PF_RDR:
case PF_NORDR:
pr = pf->prule[PF_RULESET_RDR];
break;
case PF_BINAT:
case PF_NOBINAT:
pr = pf->prule[PF_RULESET_BINAT];
break;
default:
syslog(LOG_ERR, "invalid rule action %d", r->action);
return (1);
}
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)) {
syslog(LOG_ERR, "DIOCADDRULE %m");
return (1);
}
pfctl_clear_pool(&r->rpool);
return (0);
}
int
pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)
{
struct pf_pooladdr *pa;
if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) {
syslog(LOG_ERR, "DIOCBEGINADDRS %m");
return (1);
}
pf->paddr.af = af;
TAILQ_FOREACH(pa, &p->list, entries) {
memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) {
syslog(LOG_ERR, "DIOCADDADDR %m");
return (1);
}
}
return (0);
}
void
pfctl_clear_pool(struct pf_pool *pool)
{
struct pf_pooladdr *pa;
while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
TAILQ_REMOVE(&pool->list, pa, entries);
free(pa);
}
}
int
pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
{
fprintf(stderr, "altq rules not supported in authpf\n");
return (1);
}
int
pfctl_set_optimization(struct pfctl *pf, const char *opt)
{
fprintf(stderr, "set optimization not supported in authpf\n");
return (1);
}
int
pfctl_set_logif(struct pfctl *pf, char *ifname)
{
fprintf(stderr, "set loginterface not supported in authpf\n");
return (1);
}
int
pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
{
fprintf(stderr, "set timeout not supported in authpf\n");
return (1);
}
int
pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
{
fprintf(stderr, "set limit 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)
{
fprintf(stderr, "table definitions not yet supported in authpf\n");
return (1);
}
int
pfctl_rules(int dev, char *filename, int opts, char *anchorname,
char *rulesetname)
{
/* never called, no anchors inside anchors, but we need the stub */
fprintf(stderr, "load anchor not supported from authpf\n");
return (1);
}

View File

@ -0,0 +1,37 @@
/* $OpenBSD: pathnames.h,v 1.6 2003/06/03 20:38:59 beck Exp $ */
/*
* Copyright (C) 2002 Chris Kuethe (ckuethe@ualberta.ca)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define PATH_CONFFILE "/etc/authpf/authpf.conf"
#define PATH_ALLOWFILE "/etc/authpf/authpf.allow"
#define PATH_PFRULES "/etc/authpf/authpf.rules"
#define PATH_PROBLEM "/etc/authpf/authpf.problem"
#define PATH_MESSAGE "/etc/authpf/authpf.message"
#define PATH_USER_DIR "/etc/authpf/users"
#define PATH_BAN_DIR "/etc/authpf/banned"
#define PATH_DEVFILE "/dev/pf"
#define PATH_PIDFILE "/var/authpf"
#define PATH_AUTHPF_SHELL "/usr/sbin/authpf"

View File

@ -0,0 +1,253 @@
.\" $OpenBSD: ftp-proxy.8,v 1.37 2003/09/05 12:27:47 jmc Exp $
.\"
.\" Copyright (c) 1996-2001
.\" Obtuse Systems Corporation, All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY OBTUSE SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL OBTUSE OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd August 17, 2001
.Dt FTP-PROXY 8
.Os
.Sh NAME
.Nm ftp-proxy
.Nd Internet File Transfer Protocol proxy server
.Sh SYNOPSIS
.Nm ftp-proxy
.Op Fl AnrVw
.Op Fl D Ar debuglevel
.Op Fl g Ar group
.Op Fl m Ar minport
.Op Fl M Ar maxport
.Op Fl t Ar timeout
.Op Fl u Ar user
.Sh DESCRIPTION
.Nm
is a proxy for the Internet File Transfer Protocol.
The proxy uses
.Xr pf 4
and expects to have the FTP control connection as described in
.Xr services 5
redirected to it via a
.Xr pf 4
.Em rdr
command.
An example of how to do that is further down in this document.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl A
Permit only anonymous FTP connections.
The proxy will allow connections to log in to other sites as the user
.Qq ftp
or
.Qq anonymous
only.
Any attempt to log in as another user will be blocked by the proxy.
.It Fl D Ar debuglevel
Specify a debug level, where the proxy emits verbose debug output
into
.Xr syslogd 8
at level
.Dv LOG_DEBUG .
Meaningful values of debuglevel are 0-3, where 0 is no debug output and
3 is lots of debug output, the default being 0.
.It Fl g Ar group
Specify the named group to drop group privileges to, after doing
.Xr pf 4
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.
The default is
.Dv IPPORT_HILASTAUTO
defined in
.Aq Pa netinet/in.h
as 65535.
.It Fl n
Activate network address translation
.Pq NAT
mode.
In this mode, the proxy will not attempt to proxy passive mode
.Pq PASV or EPSV
data connections.
In order for this to work, the machine running the proxy will need to
be forwarding packets and doing network address translation to allow
the outbound passive connections from the client to reach the server.
See
.Xr pf.conf 5
for more details on NAT.
The proxy only ignores passive mode data connections when using this flag;
it will still proxy PORT and EPRT mode data connections.
Without this flag,
.Nm
does not require any IP forwarding or NAT beyond the
.Em rdr
necessary to capture the FTP control connection.
.It Fl r
Use reverse host
.Pq reverse DNS
lookups for logging and libwrap use.
By default,
the proxy does not look up hostnames for libwrap or logging purposes.
.It Fl t Ar timeout
Specifies a timeout, in seconds.
The proxy will exit and close open connections if it sees no data
for the duration of the timeout.
The default is 0, which means the proxy will not time out.
.It Fl u Ar user
Specify the named user to drop privilege to, after doing
.Xr pf 4
lookups which require root privilege.
By default,
.Nm
drops privilege to the user
.Em proxy .
.Pp
Running as root means that the source of data connections the proxy makes
for PORT and EPRT will be the RFC mandated port 20.
When running as a non-root user, the source of the data connections from
.Nm
will be chosen randomly from the range
.Ar minport
to
.Ar maxport
as described above.
.It Fl V
Be verbose.
With this option the proxy logs the control commands
sent by clients and the replies sent by the servers to
.Xr syslogd 8 .
.It Fl w
Use the tcp wrapper access control library
.Xr hosts_access 3 ,
allowing connections to be allowed or denied based on the tcp wrapper's
.Xr hosts.allow 5
and
.Xr hosts.deny 5
files.
The proxy does libwrap operations after determining the destination
of the captured control connection, so that tcp wrapper rules may
be written based on the destination as well as the source of FTP connections.
.El
.Pp
.Nm ftp-proxy
is run from
.Xr inetd 8
and requires that FTP connections are redirected to it using a
.Em rdr
rule.
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
.Ed
.Pp
.Xr inetd 8
must then be configured to run
.Nm
on the port from above using
.Bd -literal -offset 2n
127.0.0.1:8021 stream tcp nowait root /usr/libexec/ftp-proxy ftp-proxy
.Ed
.Pp
in
.Xr inetd.conf 5 .
.Pp
.Nm
accepts the redirected control connections and forwards them
to the server.
The proxy replaces the address and port number that the client
sends through the control connection to the server with its own
address and proxy port, where it listens for the data connection.
When the server opens the data connection back to this port, the
proxy forwards it to the client.
The
.Xr pf.conf 5
rules need to let pass connections to these proxy ports
(see options
.Fl u , m ,
and
.Fl M
above) in on the external interface.
The following example allows only ports 49152 to 65535 to pass in
statefully:
.Bd -literal -offset indent
block in on $ext_if proto tcp all
pass in on $ext_if inet proto tcp from any to $ext_if \e
port > 49151 keep state
.Ed
.Pp
Alternatively, rules can make use of the fact that by default,
.Nm
runs as user
.Qq proxy
to allow the backchannel connections, as in the following example:
.Bd -literal -offset indent
block in on $ext_if proto tcp all
pass in on $ext_if inet proto tcp from any to $ext_if \e
user proxy keep state
.Ed
.Pp
These examples do not cover the connections from the proxy to the
foreign FTP server.
If one does not pass outgoing connections by default additional rules
are needed.
.Sh SEE ALSO
.Xr ftp 1 ,
.Xr pf 4 ,
.Xr hosts.allow 5 ,
.Xr hosts.deny 5 ,
.Xr inetd.conf 5 ,
.Xr pf.conf 5 ,
.Xr inetd 8 ,
.Xr pfctl 8 ,
.Xr syslogd 8
.Sh BUGS
Extended Passive mode
.Pq EPSV
is not supported by the proxy and will not work unless the proxy is run
in network address translation mode.
When not in network address translation mode, the proxy returns an error
to the client, hopefully forcing the client to revert to passive mode
.Pq PASV
which is supported.
EPSV will work in network address translation mode, assuming a
.Xr pf.conf 5
setup which allows the EPSV connections through to their destinations.
.Pp
IPv6 is not yet supported.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,259 @@
/* $OpenBSD: getline.c,v 1.15 2003/06/28 01:04:57 deraadt Exp $ */
/*
* Copyright (c) 1985, 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>
#include "util.h"
int refill_buffer(struct csiob *iobp);
/*
* Refill the io buffer if we KNOW that data is available
*
* Returns 1 if any new data was obtained, 0 otherwise.
*/
int
refill_buffer(struct csiob *iobp)
{
int rqlen, rlen;
if (!(iobp->data_available))
return(0);
if (iobp->got_eof)
return(0);
/*
* The buffer has been entirely consumed if next_byte == io_buffer_len.
* Otherwise, there is some still-to-be-used data in io_buffer.
* Shuffle it to the start of the buffer.
* Note that next_byte will never exceed io_buffer_len.
* Also, note that we MUST use bcopy because the two regions could
* overlap (memcpy isn't defined to work properly with overlapping
* regions).
*/
if (iobp->next_byte < iobp->io_buffer_len) {
int dst_ix = 0;
int src_ix = iobp->next_byte;
int amount = iobp->io_buffer_len - iobp->next_byte;
bcopy(&iobp->io_buffer[src_ix], &iobp->io_buffer[dst_ix],
amount);
iobp->io_buffer_len = amount;
} else if (iobp->next_byte == iobp->io_buffer_len)
iobp->io_buffer_len = 0;
else {
syslog(LOG_ERR, "next_byte(%d) > io_buffer_len(%d)",
iobp->next_byte, iobp->io_buffer_len);
exit(EX_OSERR);
}
iobp->next_byte = 0;
/* don't do tiny reads, grow first if we need to */
rqlen = iobp->io_buffer_size - iobp->io_buffer_len;
if (rqlen <= 128) {
char *tmp;
iobp->io_buffer_size += 128;
tmp = realloc(iobp->io_buffer, iobp->io_buffer_size);
if (tmp == NULL) {
syslog(LOG_INFO, "Insufficient memory");
exit(EX_UNAVAILABLE);
}
iobp->io_buffer = tmp;
rqlen = iobp->io_buffer_size - iobp->io_buffer_len;
}
/*
* Always leave an unused byte at the end of the buffer
* because the debug output uses that byte from time to time
* to ensure that something that is being printed is \0 terminated.
*/
rqlen -= 1;
doread:
rlen = read(iobp->fd, &iobp->io_buffer[iobp->io_buffer_len], rqlen);
iobp->data_available = 0;
switch (rlen) {
case -1:
if (errno == EAGAIN || errno == EINTR)
goto doread;
if (errno != ECONNRESET) {
syslog(LOG_INFO, "read() failed on socket from %s (%m)",
iobp->who);
exit(EX_DATAERR);
}
/* fall through to EOF case */
case 0:
iobp->got_eof = 1;
return(0);
break;
default:
iobp->io_buffer_len += rlen;
break;
}
return(1);
}
/*
* telnet_getline - a hacked up version of fgets to ignore TELNET escape codes.
*
* This code is derived from the getline routine found in the UC Berkeley
* ftpd code.
*
*/
int
telnet_getline(struct csiob *iobp, struct csiob *telnet_passthrough)
{
unsigned char ch;
int ix;
char tbuf[100];
iobp->line_buffer[0] = '\0';
/*
* If the buffer is empty then refill it right away.
*/
if (iobp->next_byte == iobp->io_buffer_len)
if (!refill_buffer(iobp))
return(0);
/*
* Is there a telnet command in the buffer?
*/
ch = iobp->io_buffer[iobp->next_byte];
if (ch == IAC) {
/*
* Yes - buffer must have at least three bytes in it
*/
if (iobp->io_buffer_len - iobp->next_byte < 3) {
if (!refill_buffer(iobp))
return(0);
if (iobp->io_buffer_len - iobp->next_byte < 3)
return(0);
}
iobp->next_byte++;
ch = iobp->io_buffer[iobp->next_byte++];
switch (ch) {
case WILL:
case WONT:
case DO:
case DONT:
tbuf[0] = IAC;
tbuf[1] = ch;
tbuf[2] = iobp->io_buffer[iobp->next_byte++];
(void)send(telnet_passthrough->fd, tbuf, 3,
telnet_passthrough->send_oob_flags);
break;
case IAC:
break;
default:
break;
}
return(1);
} else {
int clen;
/*
* Is there a newline in the buffer?
*/
for (ix = iobp->next_byte; ix < iobp->io_buffer_len;
ix += 1) {
if (iobp->io_buffer[ix] == '\n')
break;
if (iobp->io_buffer[ix] == '\0') {
syslog(LOG_INFO,
"got NUL byte from %s - bye!",
iobp->who);
exit(EX_DATAERR);
}
}
if (ix == iobp->io_buffer_len) {
if (!refill_buffer(iobp))
return(0);
/*
* Empty line returned
* will try again soon!
*/
return(1);
}
/*
* Expand the line buffer if it isn't big enough. We
* use a fudge factor of 5 rather than trying to
* figure out exactly how to account for the '\0 \r\n' and
* such. The correct fudge factor is 0, 1 or 2 but
* anything higher also works. We also grow it by a
* bunch to avoid having to do this often. Yes this is
* nasty.
*/
if (ix - iobp->next_byte > iobp->line_buffer_size - 5) {
char *tmp;
iobp->line_buffer_size = 256 + ix - iobp->next_byte;
tmp = realloc(iobp->line_buffer,
iobp->line_buffer_size);
if (tmp == NULL) {
syslog(LOG_INFO, "Insufficient memory");
exit(EX_UNAVAILABLE);
}
iobp->line_buffer = tmp;
}
/* +1 is for the newline */
clen = (ix+1) - iobp->next_byte;
memcpy(iobp->line_buffer, &iobp->io_buffer[iobp->next_byte],
clen);
iobp->next_byte += clen;
iobp->line_buffer[clen] = '\0';
return(1);
}
}

296
contrib/pf/ftp-proxy/util.c Normal file
View File

@ -0,0 +1,296 @@
/* $OpenBSD: util.c,v 1.16 2003/06/28 01:04:57 deraadt Exp $ */
/*
* Copyright (c) 1996-2001
* Obtuse Systems Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Obtuse Systems nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE OBTUSE SYSTEMS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OBTUSE
* SYSTEMS CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>
#include "util.h"
int Debug_Level;
int Use_Rdns;
void debuglog(int debug_level, const char *fmt, ...);
void
debuglog(int debug_level, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Debug_Level >= debug_level)
vsyslog(LOG_DEBUG, fmt, ap);
va_end(ap);
}
int
get_proxy_env(int connected_fd, struct sockaddr_in *real_server_sa_ptr,
struct sockaddr_in *client_sa_ptr)
{
struct pfioc_natlook natlook;
int slen, fd;
slen = sizeof(*real_server_sa_ptr);
if (getsockname(connected_fd, (struct sockaddr *)real_server_sa_ptr,
&slen) != 0) {
syslog(LOG_ERR, "getsockname() failed (%m)");
return(-1);
}
slen = sizeof(*client_sa_ptr);
if (getpeername(connected_fd, (struct sockaddr *)client_sa_ptr,
&slen) != 0) {
syslog(LOG_ERR, "getpeername() failed (%m)");
return(-1);
}
/*
* Build up the pf natlook structure.
* Just for IPv4 right now
*/
memset((void *)&natlook, 0, sizeof(natlook));
natlook.af = AF_INET;
natlook.saddr.addr32[0] = client_sa_ptr->sin_addr.s_addr;
natlook.daddr.addr32[0] = real_server_sa_ptr->sin_addr.s_addr;
natlook.proto = IPPROTO_TCP;
natlook.sport = client_sa_ptr->sin_port;
natlook.dport = real_server_sa_ptr->sin_port;
natlook.direction = PF_OUT;
/*
* Open the pf device and lookup the mapping pair to find
* the original address we were supposed to connect to.
*/
fd = open("/dev/pf", O_RDWR);
if (fd == -1) {
syslog(LOG_ERR, "cannot open /dev/pf (%m)");
exit(EX_UNAVAILABLE);
}
if (ioctl(fd, DIOCNATLOOK, &natlook) == -1) {
syslog(LOG_INFO,
"pf nat lookup failed %s:%hu (%m)",
inet_ntoa(client_sa_ptr->sin_addr),
ntohs(client_sa_ptr->sin_port));
close(fd);
return(-1);
}
close(fd);
/*
* Now jam the original address and port back into the into
* destination sockaddr_in for the proxy to deal with.
*/
memset((void *)real_server_sa_ptr, 0, sizeof(struct sockaddr_in));
real_server_sa_ptr->sin_port = natlook.rdport;
real_server_sa_ptr->sin_addr.s_addr = natlook.rdaddr.addr32[0];
real_server_sa_ptr->sin_len = sizeof(struct sockaddr_in);
real_server_sa_ptr->sin_family = AF_INET;
return(0);
}
/*
* Transfer one unit of data across a pair of sockets
*
* A unit of data is as much as we get with a single read(2) call.
*/
int
xfer_data(const char *what_read,int from_fd, int to_fd, struct in_addr from,
struct in_addr to)
{
int rlen, offset, xerrno, mark, flags = 0;
char tbuf[4096];
/*
* Are we at the OOB mark?
*/
if (ioctl(from_fd, SIOCATMARK, &mark) < 0) {
xerrno = errno;
syslog(LOG_ERR, "cannot ioctl(SIOCATMARK) socket from %s (%m)",
what_read);
errno = xerrno;
return(-1);
}
if (mark)
flags = MSG_OOB; /* Yes - at the OOB mark */
snarf:
rlen = recv(from_fd, tbuf, sizeof(tbuf), flags);
if (rlen == -1 && flags == MSG_OOB && errno == EINVAL) {
/* OOB didn't work */
flags = 0;
rlen = recv(from_fd, tbuf, sizeof(tbuf), flags);
}
if (rlen == 0) {
debuglog(3, "EOF on read socket");
return(0);
} else if (rlen == -1) {
if (errno == EAGAIN || errno == EINTR)
goto snarf;
xerrno = errno;
syslog(LOG_ERR, "xfer_data (%s): failed (%m) with flags 0%o",
what_read, flags);
errno = xerrno;
return(-1);
} else {
offset = 0;
debuglog(3, "got %d bytes from socket", rlen);
while (offset < rlen) {
int wlen;
fling:
wlen = send(to_fd, &tbuf[offset], rlen - offset,
flags);
if (wlen == 0) {
debuglog(3, "zero-length write");
goto fling;
} else if (wlen == -1) {
if (errno == EAGAIN || errno == EINTR)
goto fling;
xerrno = errno;
syslog(LOG_INFO, "write failed (%m)");
errno = xerrno;
return(-1);
} else {
debuglog(3, "wrote %d bytes to socket",wlen);
offset += wlen;
}
}
return(offset);
}
}
/*
* get_backchannel_socket gets us a socket bound somewhere in a
* particular range of ports
*/
int
get_backchannel_socket(int type, int min_port, int max_port, int start_port,
int direction, struct sockaddr_in *sap)
{
int count;
/*
* Make sure that direction is 'defined' and that min_port is not
* greater than max_port.
*/
if (direction != -1)
direction = 1;
/* by default we go up by one port until we find one */
if (min_port > max_port) {
errno = EINVAL;
return(-1);
}
count = 1 + max_port - min_port;
/*
* Pick a port we can bind to from within the range we want.
* If the caller specifies -1 as the starting port number then
* we pick one somewhere in the range to try.
* This is an optimization intended to speedup port selection and
* has NOTHING to do with security.
*/
if (start_port == -1)
start_port = (arc4random() % count) + min_port;
if (start_port < min_port || start_port > max_port) {
errno = EINVAL;
return(-1);
}
while (count-- > 0) {
struct sockaddr_in sa;
int one, fd;
fd = socket(AF_INET, type, 0);
bzero(&sa, sizeof sa);
sa.sin_family = AF_INET;
if (sap == NULL)
sa.sin_addr.s_addr = INADDR_ANY;
else
sa.sin_addr.s_addr = sap->sin_addr.s_addr;
/*
* Indicate that we want to reuse a port if it happens that the
* port in question was a listen port recently.
*/
one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one)) == -1)
return(-1);
sa.sin_port = htons(start_port);
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0) {
if (sap != NULL)
*sap = sa;
return(fd);
}
if (errno != EADDRINUSE)
return(-1);
/* if it's in use, try the next port */
close(fd);
start_port += direction;
if (start_port < min_port)
start_port = max_port;
else if (start_port > max_port)
start_port = min_port;
}
errno = EAGAIN;
return(-1);
}

View File

@ -0,0 +1,68 @@
/* $OpenBSD: util.h,v 1.3 2002/05/23 10:22:14 deraadt Exp $ */
/*
* Copyright (c) 1996-2001
* Obtuse Systems Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the Obtuse Systems nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL OBTUSE SYSTEMS CORPORATION OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
struct proxy_channel {
int pc_to_fd, pc_from_fd;
int pc_alive;
int pc_nextbyte;
int pc_flags;
int pc_length;
int pc_size;
struct sockaddr_in pc_from_sa, pc_to_sa;
int (*pc_filter)( void ** databuf, int datalen);
char *pc_buffer;
};
struct csiob {
int fd;
int line_buffer_size, io_buffer_size, io_buffer_len, next_byte;
unsigned char *io_buffer, *line_buffer;
struct sockaddr_in sa, real_sa;
char *who;
char alive, got_eof, data_available;
int send_oob_flags;
};
extern int telnet_getline(struct csiob *iobp,
struct csiob *telnet_passthrough);
extern int get_proxy_env(int fd, struct sockaddr_in *server_sa_ptr,
struct sockaddr_in *client_sa_ptr);
extern int get_backchannel_socket(int type, int min_port, int max_port,
int start_port, int direction, struct sockaddr_in *sap);
extern int xfer_data(const char *what_read, int from_fd, int to_fd,
struct in_addr from, struct in_addr to);
extern char *ProgName;

703
contrib/pf/man/pf.4 Normal file
View File

@ -0,0 +1,703 @@
.\" $OpenBSD: pf.4,v 1.37 2003/08/28 09:41:22 jmc Exp $
.\"
.\" Copyright (C) 2001, Kjell Wooding. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the project nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd June 24, 2001
.Dt PF 4
.Os
.Sh NAME
.Nm pf
.Nd packet filter
.Sh SYNOPSIS
.Cd "pseudo-device pf 1"
.Sh DESCRIPTION
Packet filtering takes place in the kernel.
A pseudo-device,
.Pa /dev/pf ,
allows userland processes to control the
behavior of the packet filter through an
.Xr ioctl 2
interface.
There are commands to enable and disable the filter, load rulesets,
add and remove individual rules or state table entries,
and retrieve statistics.
The most commonly used functions are covered by
.Xr pfctl 8 .
.Pp
Manipulations like loading a ruleset that involve more than a single
ioctl call require a so-called ticket, which prevents the occurrence of
multiple concurrent manipulations.
.Pp
Fields of ioctl parameter structures that refer to packet data (like
addresses and ports) are generally expected in network byte-order.
.Sh FILES
.Bl -tag -width /dev/pf -compact
.It Pa /dev/pf
packet filtering device.
.El
.Sh IOCTL INTERFACE
pf supports the following
.Xr ioctl 2
commands:
.Bl -tag -width xxxxxx
.It Dv DIOCSTART
Starts the packet filter.
.It Dv DIOCSTOP
Stops the packet filter.
.It Dv DIOCSTARTALTQ
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"
.Bd -literal
struct pfioc_pooladdr {
u_int32_t action;
u_int32_t ticket;
u_int32_t nr;
u_int32_t r_num;
u_int8_t r_action;
u_int8_t r_last;
u_int8_t af;
char anchor[PF_ANCHOR_NAME_SIZE];
char ruleset[PF_RULESET_NAME_SIZE];
struct pf_pooladdr addr;
};
.Ed
.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 {
u_int32_t action;
u_int32_t ticket;
u_int32_t pool_ticket;
u_int32_t nr;
char anchor[PF_ANCHOR_NAME_SIZE];
char ruleset[PF_RULESET_NAME_SIZE];
struct pf_rule rule;
};
.Ed
.Pp
Adds
.Va rule
at the end of the inactive ruleset.
Requires
.Va ticket
obtained through preceding DIOCBEGINRULES call, and
.Va pool_ticket
obtained through DIOCBEGINADDRS call.
DIOCADDADDR must also be called if any pool addresses are required.
The optional
.Va anchor
and
.Va ruleset
names indicate the anchor and ruleset in which to append the rule.
.Va nr
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 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
for subsequent DIOCGETRULE calls and
.Va nr
of rules in the active ruleset.
.It Dv DIOCGETRULE Fa "struct pfioc_rule"
Returns
.Va rule
number
.Va nr
using
.Va ticket
obtained through a preceding DIOCGETRULES call.
.It Dv DIOCGETADDRS Fa "struct pfioc_pooladdr"
Returns
.Va ticket
for subsequent DIOCGETADDR calls and
.Va nr
of pool addresses in the rule specified with
.Va r_action ,
.Va r_num ,
.Va anchor
and
.Va ruleset .
.It Dv DIOCGETADDR Fa "struct pfioc_pooladdr"
Returns pool address
.Va addr
number
.Va nr
from the rule specified with
.Va r_action ,
.Va r_num ,
.Va anchor
and
.Va ruleset
using
.Va ticket
obtained through a preceding DIOCGETADDRS call.
.It Dv DIOCGETALTQS Fa "struct pfioc_altq"
Returns
.Va ticket
for subsequent DIOCGETALTQ calls and
.Va nr
of queues in the active list.
.It Dv DIOCGETALTQ Fa "struct pfioc_altq"
Returns
.Va altq
number
.Va nr
using
.Va ticket
obtained through a preceding DIOCGETALTQS call.
.It Dv DIOCGETQSTATS Fa "struct pfioc_qstats"
Returns statistics on a queue.
.Bd -literal
struct pfioc_qstats {
u_int32_t ticket;
u_int32_t nr;
void *buf;
int nbytes;
u_int8_t scheduler;
};
.Ed
.Pp
A pointer to a buffer of statistics
.Va buf
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"
.Bd -literal
struct pfioc_state {
u_int32_t nr;
struct pf_state state;
};
.Ed
.Pp
Extracts the entry with the specified number from the state table.
.It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill"
Removes matching entries from the state table.
Returns the number of killed states in psk_af.
.Bd -literal
struct pfioc_state_kill {
int psk_af;
int psk_proto;
struct pf_rule_addr psk_src;
struct pf_rule_addr psk_dst;
};
.Ed
.It Dv DIOCSETSTATUSIF Fa "struct pfioc_if"
.Bd -literal
struct pfioc_if {
char ifname[IFNAMSIZ];
};
.Ed
.Pp
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;
};
.Ed
.Pp
Gets the internal packet filter statistics.
.It Dv DIOCCLRSTATUS
Clears the internal packet filter statistics.
.It Dv DIOCNATLOOK Fa "struct pfioc_natlook"
Looks up a state table entry by source and destination addresses and ports.
.Bd -literal
struct pfioc_natlook {
struct pf_addr saddr;
struct pf_addr daddr;
struct pf_addr rsaddr;
struct pf_addr rdaddr;
u_int16_t sport;
u_int16_t dport;
u_int16_t rsport;
u_int16_t rdport;
u_int8_t af;
u_int8_t proto;
u_int8_t direction;
};
.Ed
.It Dv DIOCSETDEBUG Fa "u_int32_t"
Sets the debug level.
.Bd -literal
enum { PF_DEBUG_NONE=0, PF_DEBUG_URGENT=1, PF_DEBUG_MISC=2 };
.Ed
.It Dv DIOCGETSTATES Fa "struct pfioc_states"
.Bd -literal
struct pfioc_states {
int ps_len;
union {
caddr_t psu_buf;
struct pf_state *psu_states;
} ps_u;
#define ps_buf ps_u.psu_buf
#define ps_states ps_u.psu_states
};
.Ed
.It Dv DIOCCHANGERULE Fa "struct pfioc_rule"
Adds or removes the
.Va rule
in the ruleset specified by
.Va rule.action .
.Bd -literal
enum { PF_CHANGE_ADD_HEAD=1, PF_CHANGE_ADD_TAIL=2,
PF_CHANGE_ADD_BEFORE=3, PF_CHANGE_ADD_AFTER=4,
PF_CHANGE_REMOVE=5, PF_CHANGE_GET_TICKET=6 };
.Ed
.Pp
The type of operation to be performed is indicated by
.Va action .
.Pp
.Va ticket
must be set to the value obtained with PF_CHANGE_GET_TICKET
for all actions except PF_CHANGE_GET_TICKET.
.Va pool_ticket
must be set to the value obtained with the DIOCBEGINADDRS call
for all actions except PF_CHANGE_REMOVE and PF_CHANGE_GET_TICKET.
.Pp
.Va anchor
and
.Va ruleset
indicate which anchor and ruleset the operation applies to.
.Va nr
indicates the rule number against which PF_CHANGE_ADD_BEFORE,
PF_CHANGE_ADD_AFTER or PF_CHANGE_REMOVE actions are applied.
.It Dv DIOCCHANGEADDR Fa "struct pfioc_pooladdr"
Adds or removes a pool address
.Va addr
from a rule specified with
.Va r_action ,
.Va r_num ,
.Va anchor
and
.Va ruleset .
.It Dv DIOCSETTIMEOUT Fa "struct pfioc_tm"
.Bd -literal
struct pfioc_tm {
int timeout;
int seconds;
};
.Ed
.It Dv DIOCGETTIMEOUT Fa "struct pfioc_tm"
.It Dv DIOCCLRRULECTRS
Clear per-rule statistics.
.It Dv DIOCSETLIMIT Fa "struct pfioc_limit"
Sets hard limits on the memory pools used by the packet filter.
.Bd -literal
struct pfioc_limit {
int index;
unsigned limit;
};
.Ed
.It Dv DIOCGETLIMIT Fa "struct pfioc_limit"
.It Dv DIOCRCLRTABLES Fa "struct pfioc_table"
Clear all tables.
All the IOCTLs that manipulate radix tables
use the same structure described below.
For
.Dv DIOCRCLRTABLES, pfrio_ndel contains on exit the number
of tables deleted.
.Bd -literal
struct pfioc_table {
struct pfr_table pfrio_table;
void *pfrio_buffer;
int pfrio_esize;
int pfrio_size;
int pfrio_size2;
int pfrio_nadd;
int pfrio_ndel;
int pfrio_nchange;
int pfrio_flags;
int pfrio_ticket;
};
#define pfrio_exists pfrio_nadd
#define pfrio_nzero pfrio_nadd
#define pfrio_nmatch pfrio_nadd
#define pfrio_naddr pfrio_size2
#define pfrio_setflag pfrio_size2
#define pfrio_clrflag pfrio_nadd
.Ed
.It Dv DIOCRADDTABLES Fa "struct pfioc_table"
Creates one or more tables.
On entry, pfrio_buffer[pfrio_size] contains a table of pfr_table structures.
On exit, pfrio_nadd contains the number of tables effectively created.
.Bd -literal
struct pfr_table {
char pfrt_anchor[PF_ANCHOR_NAME_SIZE];
char pfrt_ruleset[PF_RULESET_NAME_SIZE];
char pfrt_name[PF_TABLE_NAME_SIZE];
u_int32_t pfrt_flags;
u_int8_t pfrt_fback;
};
.Ed
.It Dv DIOCRDELTABLES Fa "struct pfioc_table"
Deletes one or more tables.
On entry, pfrio_buffer[pfrio_size] contains a table of pfr_table structures.
On exit, pfrio_nadd contains the number of tables effectively deleted.
.It Dv DIOCRGETTABLES Fa "struct pfioc_table"
Get the list of all tables.
On entry, pfrio_buffer[pfrio_size] contains a valid writeable buffer for
pfr_table structures.
On exit, pfrio_size contains the number of tables written into the buffer.
If the buffer is too small, the kernel does not store anything but just
returns the required buffer size, without error.
.It Dv DIOCRGETTSTATS Fa "struct pfioc_table"
Like
.Dv DIOCRGETTABLES ,
but returns an array of pfr_tstats structures.
.Bd -literal
struct pfr_tstats {
struct pfr_table pfrts_t;
u_int64_t pfrts_packets
[PFR_DIR_MAX][PFR_OP_TABLE_MAX];
u_int64_t pfrts_bytes
[PFR_DIR_MAX][PFR_OP_TABLE_MAX];
u_int64_t pfrts_match;
u_int64_t pfrts_nomatch;
long pfrts_tzero;
int pfrts_cnt;
int pfrts_refcnt[PFR_REFCNT_MAX];
};
#define pfrts_name pfrts_t.pfrt_name
#define pfrts_flags pfrts_t.pfrt_flags
.Ed
.It Dv DIOCRCLRTSTATS Fa "struct pfioc_table"
Clears the statistics of one or more tables.
On entry, pfrio_buffer[pfrio_size] contains a table of pfr_table structures.
On exit, pfrio_nzero contains the number of tables effectively cleared.
.It Dv DIOCRCLRADDRS Fa "struct pfioc_table"
Clear all addresses in a table.
On entry, pfrio_table contains the table to clear.
On exit, pfrio_ndel contains the number of addresses removed.
.It Dv DIOCRADDADDRS Fa "struct pfioc_table"
Add one or more addresses to a table.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains the list of pfr_addr structures to add.
On exit, pfrio_nadd contains the number of addresses effectively added.
.Bd -literal
struct pfr_addr {
union {
struct in_addr _pfra_ip4addr;
struct in6_addr _pfra_ip6addr;
} pfra_u;
u_int8_t pfra_af;
u_int8_t pfra_net;
u_int8_t pfra_not;
u_int8_t pfra_fback;
};
#define pfra_ip4addr pfra_u._pfra_ip4addr
#define pfra_ip6addr pfra_u._pfra_ip6addr
.Ed
.It Dv DIOCRDELADDRS Fa "struct pfioc_table"
Delete one or more addresses from a table.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains the list of pfr_addr structures to delete.
On exit, pfrio_ndel contains the number of addresses effectively deleted.
.It Dv DIOCRSETADDRS Fa "struct pfioc_table"
Replace the content of a table by a new address list.
This is the most complicated command, which uses all the structure members.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains the new list of pfr_addr structures.
In addition to that, if size2 is nonzero, pfrio_buffer[pfrio_size..pfrio_size2]
must be a writeable buffer, into which the kernel can copy the addresses that
have been deleted during the replace operation.
On exit, pfrio_ndel, pfrio_nadd and pfrio_nchange contain the number of
addresses deleted, added and changed by the kernel.
If pfrio_size2 was set on
entry, pfrio_size2 will point to the size of the buffer used, exactly like
.Dv DIOCRGETADDRS .
.It Dv DIOCRGETADDRS Fa "struct pfioc_table"
Get all the addresses of a table.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains a valid writeable buffer for pfr_addr structures.
On exit, pfrio_size contains the number of addresses written into the buffer.
If the buffer was too small, the kernel does not store anything but just
return the required buffer size, without returning an error.
.It Dv DIOCRGETASTATS Fa "struct pfioc_table"
Like
.Dv DIOCRGETADDRS ,
but returns an array of pfr_astats structures.
.Bd -literal
struct pfr_astats {
struct pfr_addr pfras_a;
u_int64_t pfras_packets
[PFR_DIR_MAX][PFR_OP_ADDR_MAX];
u_int64_t pfras_bytes
[PFR_DIR_MAX][PFR_OP_ADDR_MAX];
long pfras_tzero;
};
.Ed
.It Dv DIOCRCLRASTATS Fa "struct pfioc_table"
Clears the statistics of one or more addresses.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains a table of pfr_addr structures to clear.
On exit, pfrio_nzero contains the number of addresses effectively cleared.
.It Dv DIOCRTSTADDRS Fa "struct pfioc_table"
Test if the given addresses match a table.
On entry, pfrio_table contains the table id and pfrio_buffer[pfrio_size]
contains a table of pfr_addr structures to test.
On exit, the kernel updates the pfr_addr table by setting the pfra_fback
member appropriately.
.It Dv DIOCRSETTFLAGS Fa "struct pfioc_table"
Change the
.Va const
or
.Va persist
flag of a table.
On entry, pfrio_buffer[pfrio_size] contains a table of pfr_table structures,
and pfrio_setflag contains the flags to add, while pfrio_clrflag contains the
flags to remove.
On exit, pfrio_nchange and pfrio_ndel contain the number of tables altered
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]
contains the list of pfr_addr structures to put in the table.
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 DIOCFPFLUSH
Flush the passive OS fingerprint table.
.It Dv DIOCFPADD Fa "struct pf_osfp_ioctl"
.Bd -literal
struct pf_osfp_ioctl {
struct pf_osfp_entry {
SLIST_ENTRY(pf_osfp_entry) fp_entry;
pf_osfp_t fp_os;
char fp_class_nm[PF_OSFP_LEN];
char fp_version_nm[PF_OSFP_LEN];
char fp_subtype_nm[PF_OSFP_LEN];
} fp_os;
u_int16_t fp_mss;
u_int16_t fp_wsize;
u_int16_t fp_psize;
u_int8_t fp_ttl;
u_int8_t fp_wscale;
u_int8_t fp_flags;
int fp_getnum;
};
.Ed
.Pp
Add a passive OS fingerprint to the table.
Set
.Va fp_os.fp_os
to the packed fingerprint,
.Va fp_os.fp_class_nm
to the name of the class (Linux, Windows, etc),
.Va fp_os.fp_version_nm
to the name of the version (NT, 95, 98), and
.Va fp_os.fp_subtype_nm
to the name of the subtype or patchlevel.
The members
.Va fp_mss ,
.Va fp_wsize ,
.Va fp_psize ,
.Va fp_ttl ,
and
.Va fp_wscale
are set to the TCP MSS, the TCP window size, the IP length and the IP TTL of
the TCP SYN packet respectively.
The
.Va fp_flags
member is filled according to the net/pfvar.h include file PF_OSFP_* defines.
The
.Va fp_getnum
is not used with this ioctl.
.Pp
The structure's slack space must be zeroed for correct operation; memset
the whole structure to zero before filling and sending to the kernel.
.It Dv DIOCFPGET Fa "struct pf_osfp_ioctl"
.Bd -literal
struct pf_osfp_ioctl {
struct pf_osfp_entry {
SLIST_ENTRY(pf_osfp_entry) fp_entry;
pf_osfp_t fp_os;
char fp_class_nm[PF_OSFP_LEN];
char fp_version_nm[PF_OSFP_LEN];
char fp_subtype_nm[PF_OSFP_LEN];
} fp_os;
u_int16_t fp_mss;
u_int16_t fp_wsize;
u_int16_t fp_psize;
u_int8_t fp_ttl;
u_int8_t fp_wscale;
u_int8_t fp_flags;
int fp_getnum;
};
.Ed
.Pp
Get the passive OS fingerprint number
.Va fp_getnum
from the kernel's fingerprint list.
The rest of the structure members will come back filled.
Get the whole list by repeatedly incrementing the
.Va fp_getnum
number until the ioctl returns EBUSY.
.El
.Sh EXAMPLES
The following example demonstrates how to use the DIOCNATLOOK command
to find the internal host/port of a NATed connection.
.Bd -literal
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
u_int32_t
read_address(const char *s)
{
int a, b, c, d;
sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d);
return htonl(a << 24 | b << 16 | c << 8 | d);
}
void
print_address(u_int32_t a)
{
a = ntohl(a);
printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255,
a >> 8 & 255, a & 255);
}
int
main(int argc, char *argv[])
{
struct pfioc_natlook nl;
int dev;
if (argc != 5) {
printf("%s <gwy addr> <gwy port> <ext addr> <ext port>\\n",
argv[0]);
return 1;
}
dev = open("/dev/pf", O_RDWR);
if (dev == -1)
err(1, "open(\\"/dev/pf\\") failed");
memset(&nl, 0, sizeof(struct pfioc_natlook));
nl.saddr.v4.s_addr = read_address(argv[1]);
nl.sport = htons(atoi(argv[2]));
nl.daddr.v4.s_addr = read_address(argv[3]);
nl.dport = htons(atoi(argv[4]));
nl.af = AF_INET;
nl.proto = IPPROTO_TCP;
nl.direction = PF_IN;
if (ioctl(dev, DIOCNATLOOK, &nl))
err(1, "DIOCNATLOOK");
printf("internal host ");
print_address(nl.rsaddr.v4.s_addr);
printf(":%u\\n", ntohs(nl.rsport));
return 0;
}
.Ed
.Sh SEE ALSO
.Xr ioctl 2 ,
.Xr bridge 4 ,
.Xr pflog 4 ,
.Xr pfsync 4 ,
.Xr pfctl 8
.Sh HISTORY
The
.Nm
packet filtering mechanism first appeared in
.Ox 3.0 .

2486
contrib/pf/man/pf.conf.5 Normal file

File diff suppressed because it is too large Load Diff

242
contrib/pf/man/pf.os.5 Normal file
View File

@ -0,0 +1,242 @@
.\" $OpenBSD: pf.os.5,v 1.4 2003/08/28 09:41:23 jmc Exp $
.\"
.\" Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd August 18, 2003
.Dt PF.OS 5
.Os
.Sh NAME
.Nm pf.os
.Nd format of the operating system fingerprints file
.Sh DESCRIPTION
The
.Xr pf 4
firewall and the
.Xr tcpdump 8
program can both fingerprint the operating system of hosts that
originate an IPv4 TCP connection.
The file consists of newline-separated records, one per fingerprint,
containing nine colon
.Pq Ql \&:
separated fields.
These fields are as follows:
.Pp
.Bl -tag -width Description -offset indent -compact
.It window
The TCP window size.
.It TTL
The IP time to live.
.It df
The presence of the IPv4 don't fragment bit.
.It packet size
The size of the initial TCP packet.
.It TCP options
An ordered list of the TCP options.
.It class
The class of operating system.
.It version
The version of the operating system.
.It subtype
The subtype of patchlevel of the operating system.
.It description
The overall textual description of the operating system, version and subtype.
.El
.Pp
The
.Ar window
field corresponds to the th->th_win field in the TCP header and is the
source host's advertised TCP window size.
It may be between zero and 65,535 inclusive.
The window size may be given as a multiple of a constant by prepending
the size with a percent sign
.Sq %
and the value will be used as a modulus.
Three special values may be used for the window size:
.Pp
.Bl -tag -width xxx -offset indent -compact
.It *
An asterisk will wildcard the value so any window size will match.
.It S
Allow any window size which is a multiple of the maximum segment size (MSS).
.It T
Allow any window size which is a multiple of the maximum transmission unit
(MTU).
.El
.Pp
The
.Ar ttl
value is the initial time to live in the IP header.
The fingerprint code will account for the volatility of the packets's TTL
as it traverses a network.
.Pp
The
.Ar df
bit corresponds to the Don't Fragment bit in an IPv4 header.
It tells intermediate routers not to fragment the packet and is used for
path MTU discovery.
It may be either a zero or a one.
.Pp
The
.Ar packet size
is the literal size of the full IP packet and is a function of all of
the IP and TCP options.
.Pp
The
.Ar TCP options
field is an ordered list of the individual TCP options that appear in the
SYN packet.
Each option is described by a single character separated by a comma and
certain ones may include a value.
The options are:
.Pp
.Bl -tag -width Description -offset indent -compact
.It Mnnn
maximum segment size (MSS) option.
The value is the maximum packet size of the network link which may
include the
.Sq %
modulus or match all MSSes with the
.Sq *
value.
.It N
the NOP option (NO Operation).
.It T[0]
the timestamp option.
Certain operating systems always start with a zero timestamp in which
case a zero value is added to the option; otherwise no value is appended.
.It S
the Selective ACKnowledgement OK (SACKOK) option.
.It Wnnn
window scaling option.
The value is the size of the window scaling which may include the
.Sq %
modulus or match all window scalings with the
.Sq *
value.
.El
.Pp
No TCP options in the fingerprint may be given with a single dot
.Sq \&. .
.Pp
An example of OpenBSD's TCP options are:
.Pp
.Dl M*,N,N,S,N,W0,N,N,T
.Pp
The first option
.Ar M*
is the MSS option and will match all values.
The second and third options
.Ar N
will match two NOPs.
The fourth option
.Ar S
will match the SACKOK option.
The fifth
.Ar N
will match another NOP.
The sixth
.Ar W0
will match a window scaling option with a zero scaling size.
The seventh and eighth
.Ar N
options will match two NOPs.
And the ninth and final option
.Ar T
will match the timestamp option with any time value.
.Pp
The TCP options in a fingerprint will only match packets with the
exact same TCP options in the same order.
.Pp
The
.Ar class
field is the class, genre or vender of the operating system.
.Pp
The
.Ar version
is the version of the operating system.
It is used to distinguish between different fingerprints of operating
systems of the same class but different versions.
.Pp
The
.Ar subtype
is the subtype or patch level of the operating system version.
It is used to distinguish between different fingerprints of operating
systems of the same class and same version but slightly different
patches or tweaking.
.Pp
The
.Ar description
is a general description of the operating system, its version,
patchlevel and any further useful details.
.Sh EXAMPLES
The fingerprint of a plain
.Ox 3.3
host is:
.Bd -literal
16384:64:1:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3::OpenBSD 3.3
.Ed
.Pp
The fingerprint of an
.Ox 3.3
host behind a PF scrubbing firewall with a no-df rule would be:
.Bd -literal
16384:64:0:64:M*,N,N,S,N,W0,N,N,T:OpenBSD:3.3:!df:OpenBSD 3.3 scrub no-df
.Ed
.Pp
An absolutely braindead embedded operating system fingerprint could be:
.Bd -literal
65535:255:0:40:.:DUMMY:1.1:p3:Dummy embedded OS v1.1p3
.Ed
.Pp
The
.Xr tcpdump 8
output of
.Bd -literal
# tcpdump -s128 -c1 -nv 'tcp[13] == 2'
03:13:48.118526 10.0.0.1.3377 > 10.0.0.0.2: S [tcp sum ok] \e
534596083:534596083(0) win 57344 <mss 1460> (DF) [tos 0x10] \e
(ttl 64, id 11315)
.Ed
.Pp
almost translates into the following fingerprint
.Bd -literal
57344:64:1:44:M1460: exampleOS:1.0::exampleOS 1.0
.Ed
.Pp
.Xr tcpdump 8
does not explicitly give the packet length.
But it can usually be derived by adding the size of the IPv4 header to
the size of the TCP header to the size of the TCP options.
The size of both headers is typically twenty each and the usual
sizes of the TCP options are:
.Pp
.Bl -tag -width timestamp -offset indent -compact
.It mss
four bytes.
.It nop
1 byte.
.It sackOK
two bytes.
.It timestamp
ten bytes.
.It wscale
three bytes.
.El
.Pp
In the above example, the packet size comes out to 44 bytes.
.Sh SEE ALSO
.Xr pf 4 ,
.Xr pf.conf 5 ,
.Xr pfctl 8 ,
.Xr tcpdump 8

88
contrib/pf/man/pflog.4 Normal file
View File

@ -0,0 +1,88 @@
.\" $OpenBSD: pflog.4,v 1.4 2003/09/22 04:53:15 jmc Exp $
.\"
.\" Copyright (c) 2001 Tobias Weingartner
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd December 10, 2001
.Dt PFLOG 4
.Os
.Sh NAME
.Nm pflog
.Nd packet filter logging interface
.Sh SYNOPSIS
.Sy pseudo-device Nm pflog Em <number>
.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
time by invoking
.Xr tcpdump 8
on the
.Nm
interface.
.Pp
Each packet retrieved on this interface has a header associated
with it of length
.Dv PFLOG_HDRLEN .
This header documents the address family, interface name, rule
number, reason, action, and direction of the packet that was logged.
This structure, defined in
.Aq Pa net/if_pflog.h
looks like
.Bd -literal -offset indent
struct pfloghdr {
u_int8_t length;
sa_family_t af;
u_int8_t action;
u_int8_t reason;
char ifname[IFNAMSIZ];
char ruleset[PF_RULESET_NAME_SIZE];
u_int32_t rulenr;
u_int32_t subrulenr;
u_int8_t dir;
u_int8_t pad[3];
};
.Ed
.Sh EXAMPLES
.Bd -literal -offset indent
# ifconfig pflog0 up
# tcpdump -n -e -ttt -i pflog0
.Ed
.Sh SEE ALSO
.Xr inet 4 ,
.Xr inet6 4 ,
.Xr netintro 4 ,
.Xr pf 4 ,
.Xr ifconfig 8 ,
.Xr pflogd 8 ,
.Xr tcpdump 8
.Sh HISTORY
The
.Nm
device first appeared in
.Ox 3.0 .
.\" .Sh BUGS
.\" Anything here?

80
contrib/pf/man/pfsync.4 Normal file
View File

@ -0,0 +1,80 @@
.\" $OpenBSD: pfsync.4,v 1.6 2003/06/06 10:29:41 jmc Exp $
.\"
.\" Copyright (c) 2002 Michael Shalayeff
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND,
.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd November 29, 2002
.Dt PFSYNC 4
.Os
.Sh NAME
.Nm pfsync
.Nd packet filter states table logging interface
.Sh SYNOPSIS
.Sy pseudo-device Nm 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
.Xr tcpdump 8
on the
.Nm
interface.
.Pp
Each packet retrieved on this interface has a header associated
with it of length
.Dv PFSYNC_HDRLEN .
The header indicates the version of the protocol, address family,
action taken on the following states and the number of state
table entries attached in this packet.
This structure, defined in
.Aq Pa net/if_pfsync.h
looks like:
.Bd -literal -offset indent
struct pfsync_header {
u_int8_t version;
u_int8_t af;
u_int8_t action;
u_int8_t count;
};
.Ed
.Sh EXAMPLES
.Bd -literal -offset indent
# ifconfig pfsync0 up
# tcpdump -s1500 -evtni pfsync0
.Ed
.Sh SEE ALSO
.Xr inet 4 ,
.Xr inet6 4 ,
.Xr netintro 4 ,
.Xr pf 4 ,
.Xr ifconfig 8 ,
.Xr tcpdump 8
.Sh HISTORY
The
.Nm
device first appeared in
.Ox 3.3 .

4489
contrib/pf/pfctl/parse.y Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,279 @@
/* $OpenBSD: pf_print_state.c,v 1.33 2003/07/06 22:01:28 deraadt Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include "pfctl_parser.h"
#include "pfctl.h"
void print_name(struct pf_addr *, sa_family_t);
void
print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
{
switch(addr->type) {
case PF_ADDR_DYNIFTL:
printf("(%s)", addr->v.ifname);
break;
case PF_ADDR_TABLE:
if (verbose)
if (addr->p.tblcnt == -1)
printf("<%s:*>", addr->v.tblname);
else
printf("<%s:%d>", addr->v.tblname,
addr->p.tblcnt);
else
printf("<%s>", addr->v.tblname);
return;
case PF_ADDR_ADDRMASK:
if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
PF_AZERO(&addr->v.a.mask, AF_INET6))
printf("any");
else {
char buf[48];
if (inet_ntop(af, &addr->v.a.addr, buf,
sizeof(buf)) == NULL)
printf("?");
else
printf("%s", buf);
}
break;
case PF_ADDR_NOROUTE:
printf("no-route");
return;
default:
printf("?");
return;
}
if (! PF_AZERO(&addr->v.a.mask, af)) {
int bits = unmask(&addr->v.a.mask, af);
if (bits != (af == AF_INET ? 32 : 128))
printf("/%d", bits);
}
}
void
print_name(struct pf_addr *addr, sa_family_t af)
{
char host[NI_MAXHOST];
strlcpy(host, "?", sizeof(host));
switch (af) {
case AF_INET: {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr = addr->v4;
getnameinfo((struct sockaddr *)&sin, sin.sin_len,
host, sizeof(host), NULL, 0, NI_NOFQDN);
break;
}
case AF_INET6: {
struct sockaddr_in6 sin6;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(sin6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = addr->v6;
getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
host, sizeof(host), NULL, 0, NI_NOFQDN);
break;
}
}
printf("%s", host);
}
void
print_host(struct pf_state_host *h, sa_family_t af, int opts)
{
u_int16_t p = ntohs(h->port);
if (opts & PF_OPT_USEDNS)
print_name(&h->addr, af);
else {
struct pf_addr_wrap aw;
memset(&aw, 0, sizeof(aw));
aw.v.a.addr = h->addr;
if (af == AF_INET)
aw.v.a.mask.addr32[0] = 0xffffffff;
else
memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
}
if (p) {
if (af == AF_INET)
printf(":%u", p);
else
printf("[%u]", p);
}
}
void
print_seq(struct pf_state_peer *p)
{
if (p->seqdiff)
printf("[%u + %u](+%u)", p->seqlo, p->seqhi - p->seqlo,
p->seqdiff);
else
printf("[%u + %u]", p->seqlo, p->seqhi - p->seqlo);
}
void
print_state(struct pf_state *s, int opts)
{
struct pf_state_peer *src, *dst;
struct protoent *p;
int min, sec;
if (s->direction == PF_OUT) {
src = &s->src;
dst = &s->dst;
} else {
src = &s->dst;
dst = &s->src;
}
if ((p = getprotobynumber(s->proto)) != NULL)
printf("%s ", p->p_name);
else
printf("%u ", s->proto);
if (PF_ANEQ(&s->lan.addr, &s->gwy.addr, s->af) ||
(s->lan.port != s->gwy.port)) {
print_host(&s->lan, s->af, opts);
if (s->direction == PF_OUT)
printf(" -> ");
else
printf(" <- ");
}
print_host(&s->gwy, s->af, opts);
if (s->direction == PF_OUT)
printf(" -> ");
else
printf(" <- ");
print_host(&s->ext, s->af, opts);
printf(" ");
if (s->proto == IPPROTO_TCP) {
if (src->state <= TCPS_TIME_WAIT &&
dst->state <= TCPS_TIME_WAIT)
printf(" %s:%s\n", tcpstates[src->state],
tcpstates[dst->state]);
else if (src->state == PF_TCPS_PROXY_SRC ||
dst->state == PF_TCPS_PROXY_SRC)
printf(" PROXY:SRC\n");
else if (src->state == PF_TCPS_PROXY_DST ||
dst->state == PF_TCPS_PROXY_DST)
printf(" PROXY:DST\n");
else
printf(" <BAD STATE LEVELS %u:%u>\n",
src->state, dst->state);
if (opts & PF_OPT_VERBOSE) {
printf(" ");
print_seq(src);
if (src->wscale && dst->wscale)
printf(" wscale %u",
src->wscale & PF_WSCALE_MASK);
printf(" ");
print_seq(dst);
if (src->wscale && dst->wscale)
printf(" wscale %u",
dst->wscale & PF_WSCALE_MASK);
printf("\n");
}
} else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
dst->state < PFUDPS_NSTATES) {
const char *states[] = PFUDPS_NAMES;
printf(" %s:%s\n", states[src->state], states[dst->state]);
} else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
dst->state < PFOTHERS_NSTATES) {
/* XXX ICMP doesn't really have state levels */
const char *states[] = PFOTHERS_NAMES;
printf(" %s:%s\n", states[src->state], states[dst->state]);
} else {
printf(" %u:%u\n", src->state, dst->state);
}
if (opts & PF_OPT_VERBOSE) {
sec = s->creation % 60;
s->creation /= 60;
min = s->creation % 60;
s->creation /= 60;
printf(" age %.2u:%.2u:%.2u", s->creation, min, sec);
sec = s->expire % 60;
s->expire /= 60;
min = s->expire % 60;
s->expire /= 60;
printf(", expires in %.2u:%.2u:%.2u", s->expire, min, sec);
printf(", %u:%u pkts, %u:%u bytes",
s->packets[0], s->packets[1], s->bytes[0], s->bytes[1]);
if (s->anchor.nr != -1)
printf(", anchor %u", s->anchor.nr);
if (s->rule.nr != -1)
printf(", rule %u", s->rule.nr);
printf("\n");
}
}
int
unmask(struct pf_addr *m, sa_family_t af)
{
int i = 31, j = 0, b = 0;
u_int32_t tmp;
while (j < 4 && m->addr32[j] == 0xffffffff) {
b += 32;
j++;
}
if (j < 4) {
tmp = ntohl(m->addr32[j]);
for (i = 31; tmp & (1 << i); --i)
b++;
}
return (b);
}

506
contrib/pf/pfctl/pfctl.8 Normal file
View File

@ -0,0 +1,506 @@
.\" $OpenBSD: pfctl.8,v 1.102 2003/09/18 09:18:51 jmc Exp $
.\"
.\" Copyright (c) 2001 Kjell Wooding. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd November 20, 2002
.Dt PFCTL 8
.Os
.Sh NAME
.Nm pfctl
.Nd "control the packet filter (PF) and network address translation (NAT) device"
.Sh SYNOPSIS
.Nm pfctl
.Bk -words
.Op Fl AdeghnNqrROvz
.Op Fl a Ar anchor Ns Op Ar :ruleset
.Op Fl D Ar macro=value
.Op Fl f Ar file
.Op Fl F Ar modifier
.Op Fl k Ar host
.Op Fl s Ar modifier
.Op Fl t Ar table
.Op Fl T Ar command Op Ar address ...
.Op Fl x Ar level
.Ek
.Sh DESCRIPTION
The
.Nm
utility communicates with the packet filter device using the
ioctl interface described in
.Xr pf 4 .
It allows ruleset and parameter configuration and retrieval of status
information from the packet filter.
.Pp
Packet filtering restricts the types of packets that pass through
network interfaces entering or leaving the host based on filter
rules as described in
.Xr pf.conf 5 .
The packet filter can also replace addresses and ports of packets.
Replacing source addresses and ports of outgoing packets is called
NAT (Network Address Translation) and is used to connect an internal
network (usually reserved address space) to an external one (the
Internet) by making all connections to external hosts appear to
come from the gateway.
Replacing destination addresses and ports of incoming packets
is used to redirect connections to different hosts and/or ports.
A combination of both translations, bidirectional NAT, is also
supported.
Translation rules are described in
.Xr pf.conf 5 .
.Pp
When the variable pf is set to YES in
.Xr rc.conf 8 ,
the rule file specified with the variable pf_rules
is loaded automatically by the
.Xr rc 8
scripts and the packet filter is enabled.
.Pp
The packet filter does not itself forward packets between interfaces.
Forwarding can be enabled by setting the
.Xr sysctl 8
variables
.Em net.inet.ip.forwarding
and/or
.Em net.inet6.ip6.forwarding ,
to 1.
Set them permanently in
.Xr sysctl.conf 5 .
.Pp
The
.Nm
utility provides several commands.
The options are as follows:
.Bl -tag -width Ds
.It Fl a Ar anchor Ns Op Ar :ruleset
Apply flags
.Fl f ,
.Fl F
and
.Fl s
only to the rules in the specified
.Ar anchor
and optional named ruleset
.Ar ruleset .
In addition to the main ruleset,
.Nm
can load and manipulate additional rulesets by name.
Named rulesets are attached at
.Ar anchor
points, which are also referenced by name.
Evaluation of
.Ar anchor
rules from the main ruleset is described in
.Xr pf.conf 5 .
For example, to show all filter rules inside anchor
.Li foo :
.Bd -literal -offset indent
# pfctl -a foo -s rules
.Ed
.Pp
Private tables can also be put inside subrulesets, either by having table
statements in the
.Xr pf.conf 5
file that is loaded in the anchor, or by using regular table commands as in:
.Bd -literal -offset indent
# pfctl -a foo:bar -t mytable -T add 1.2.3.4 5.6.7.8
.Ed
.Pp
When a rule referring to a table is loaded in an anchor, the rule will use the
private table if one is defined, and then fallback to the table defined in the
main ruleset, if there is one.
This is similar to C rules for variables.
It is possible to create distinct tables with the same name in the global
ruleset and in an anchor, but this is often bad design and a warning will be
issued in that case.
.It Fl A
Load only the queue rules present in the rule file.
Other rules and options are ignored.
.It Fl d
Disable the packet filter.
.It Fl D Ar macro=value
Define
.Ar macro
to be set to
.Ar value
on the command line.
Overrides the definition of
.Ar macro
in the ruleset.
.It Fl e
Enable the packet filter.
.It Fl f Ar file
Load the rules contained in
.Ar file .
This
.Ar file
may contain macros, tables, options, and normalization, queueing,
translation, and filtering rules.
With the exception of macros and tables, the statements must appear in that
order.
.It Fl F Ar modifier
Flush the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
.Bl -tag -width xxxxxxxxxxxx -compact
.It Fl F Ar nat
Flush the NAT rules.
.It Fl F Ar queue
Flush the queue rules.
.It Fl F Ar rules
Flush the filter rules.
.It Fl F Ar state
Flush the state table (NAT and filter).
.It Fl F Ar info
Flush the filter information (statistics that are not bound to rules).
.It Fl F Ar Tables
Flush the tables.
.It Fl F Ar osfp
Flush the passive operating system fingerprints.
.It Fl F Ar all
Flush all of the above.
.El
.It Fl g
Include output helpful for debugging.
.It Fl k Ar host
Kill all of the state entries originating from the specified
.Ar host .
A second
.Fl k Ar host
option may be specified, which will kill all the state entries
from the first
.Ar host
to the second
.Ar host .
For example, to kill all of the state entries originating from
.Li host :
.Bd -literal -offset indent
# pfctl -k host
.Ed
.Pp
To kill all of the state entries from
.Li host1
to
.Li host2 :
.Bd -literal -offset indent
# pfctl -k host1 -k host2
.Ed
.It Fl h
Help.
.It Fl n
Do not actually load rules, just parse them.
.It Fl N
Load only the NAT rules present in the rule file.
Other rules and options are ignored.
.It Fl q
Only print errors and warnings.
.It Fl r
Perform reverse DNS lookups on states when displaying them.
.It Fl R
Load only the filter rules present in the rule file.
Other rules and options are ignored.
.It Fl O
Load only the options present in the rule file.
Other rules and options are ignored.
.It Fl s Ar modifier
Show the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
.Bl -tag -width xxxxxxxxxxxx -compact
.It Fl s Ar nat
Show the currently loaded NAT rules.
.It Fl s Ar queue
Show the currently loaded queue rules.
When used together with
.Fl v ,
per-queue statistics are also shown.
When used together with
.Fl v v ,
.Nm
will loop and show updated queue statistics every five seconds, including
measured bandwidth and packets per second.
.It Fl s Ar rules
Show the currently loaded filter rules.
When used together with
.Fl v ,
the per-rule statistics (number of evaluations,
packets and bytes) are also shown.
Note that the 'skip step' optimization done automatically by the kernel
will skip evaluation of rules where possible.
Packets passed statefully are counted in the rule that created the state
(even though the rule isn't evaluated more than once for the entire
connection).
.It Fl s Ar Anchors
Show the currently loaded anchors.
If
.Fl a Ar anchor
is specified as well, the named rulesets currently loaded in the specified
anchor are shown instead.
.It Fl s Ar state
Show the contents of the state table.
.It Fl s Ar info
Show filter information (statistics and counters).
.It Fl s Ar labels
Show per-rule statistics (label, evaluations, packets, bytes) of
filter rules with labels, useful for accounting.
.It Fl s Ar timeouts
Show the current global timeouts.
.It Fl s Ar memory
Show the current pool memory hard limits.
.It Fl s Ar Tables
Show the list of tables.
.It Fl s Ar osfp
Show the list of operating system fingerprints.
Can be used in combination with
.Fl o Ar file
to list the fingerprints in a
.Xr pf.os 5
file.
.It Fl s Ar all
Show all of the above.
.El
.It Fl t Ar table
Specify the name of the table.
.It Fl T Ar command Op Ar address ...
Specify the
.Ar command
(may be abbreviated) to apply to the table.
Commands include:
.Pp
.Bl -tag -width xxxxxxxxxxxx -compact
.It Fl T Ar kill
Kill a table.
.It Fl T Ar flush
Flush all addresses of a table.
.It Fl T Ar add
Add one or more addresses in a table.
Automatically create a nonexisting table.
.It Fl T Ar delete
Delete one or more addresses from a table.
.It Fl T Ar replace
Replace the addresses of the table.
Automatically create a nonexisting table.
.It Fl T Ar show
Show the content (addresses) of a table.
.It Fl T Ar test
Test if the given addresses match a table.
.It Fl T Ar zero
Clear all the statistics of a table.
.It Fl T Ar load
Load only the table definitions from
.Xr pf.conf 5 .
This is used in conjunction with the
.Fl f
flag, as in:
.Bd -literal -offset indent
# pfctl -Tl -f pf.conf
.Ed
.El
.Pp
For the
.Ar add ,
.Ar delete ,
.Ar replace
and
.Ar test
commands, the list of addresses can be specified either directly on the command
line and/or in an unformatted text file, using the
.Fl f
flag.
Comments starting with a "#" are allowed in the text file.
With these commands, the
.Fl v
flag can also be used once or twice, in which case
.Nm pfctl
will print the
detailed result of the operation for each individual address, prefixed by
one of the following letters:
.Pp
.Bl -tag -width XXX -compact
.It A
The address/network has been added.
.It C
The address/network has been changed (negated).
.It D
The address/network has been deleted.
.It M
The address matches (test operation only).
.It X
The address/network is duplicated and therefore ignored.
.It Y
The address/network cannot be added/deleted due to conflicting "!" attribute.
.It Z
The address/network has been cleared (statistics).
.El
.Pp
Each table maintains a set of counters that can be retrieved using the
.Fl v
flag of
.Nm pfctl .
For example, the following commands define a wide open firewall which will keep
track of packets going to or coming from the
.Ox
ftp server.
The following commands configure the firewall and send 10 pings to the ftp
server:
.Bd -literal -offset indent
# printf \&"table <test> { ftp.openbsd.org }\en \e
\ \ pass out to <test> keep state\en" \&| pfctl -f-
# ping -qc10 ftp.openbsd.org
.Ed
.Pp
We can now use the table
.Ar show
command to output, for each address and packet direction, the number of packets
and bytes that are being passed or blocked by rules referencing the table.
The time at which the current accounting started is also shown with the
.Ar Cleared
line.
.Bd -literal -offset indent
# pfctl -t test -vTshow
\ \ \ 129.128.5.191
\ \ \ \ Cleared: \ \ \ \ Thu Feb 13 18:55:18 2003
\ \ \ \ In/Block: \ \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
\ \ \ \ In/Pass: \ \ \ \ [ Packets: 10 \ \ \ \ \ \ Bytes: 840 \ \ \ \ \ ]
\ \ \ \ Out/Block: \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
\ \ \ \ Out/Pass: \ \ \ [ Packets: 10 \ \ \ \ \ \ Bytes: 840 \ \ \ \ \ ]
.Ed
.Pp
Similarly, it is possible to view global information about the tables
by using the
.Fl v
modifier twice and the
.Ar show Tables
command.
This will display the number of addresses on each table,
the number of rules which reference the table, and the global
packet statistics for the whole table:
.Bd -literal -offset indent
# pfctl -vvsTables
--a-r- test
\ \ \ \ Addresses: \ \ 1
\ \ \ \ Cleared: \ \ \ \ Thu Feb 13 18:55:18 2003
\ \ \ \ References: \ [ Anchors: 0 \ \ \ \ \ \ \ Rules: 1 \ \ \ \ \ \ \ ]
\ \ \ \ Evaluations: [ NoMatch: 3496 \ \ \ \ Match: 1 \ \ \ \ \ \ \ ]
\ \ \ \ In/Block: \ \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
\ \ \ \ In/Pass: \ \ \ \ [ Packets: 10 \ \ \ \ \ \ Bytes: 840 \ \ \ \ \ ]
\ \ \ \ In/XPass: \ \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
\ \ \ \ Out/Block: \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
\ \ \ \ Out/Pass: \ \ \ [ Packets: 10 \ \ \ \ \ \ Bytes: 840 \ \ \ \ \ ]
\ \ \ \ Out/XPass: \ \ [ Packets: 0 \ \ \ \ \ \ \ Bytes: 0 \ \ \ \ \ \ \ ]
.Ed
.Pp
As we can see here, only one packet - the initial ping request - matched the
table; but all packets passing as the result of the state are correctly
accounted for.
Reloading the table(s) or ruleset will not affect packet accounting in any way.
The two
.Ar XPass
counters are incremented instead of the
.Ar Pass
counters when a \&"stateful\&" packet is passed but doesn't match the table
anymore.
This will happen in our example if someone flushes the table while the ping
command is running.
.Pp
When used with a single
.Fl v ,
.Nm pfctl
will only display the first line containing the table flags and name.
The flags are defined as follows:
.Pp
.Bl -tag -width XXX -compact
.It c
For constant tables, which cannot be altered outside
.Xr pf.conf 5 .
.It p
For persistent tables, which don't get automatically flushed when no rules
refer to them.
.It a
For tables which are part of the
.Ar active
tableset.
Tables without this flag do not really exist, cannot contain addresses, and are
only listed if the
.Fl g
flag is given.
.It i
For tables which are part of the
.Ar inactive
tableset.
This flag can only be witnessed briefly during the loading of
.Xr pf.conf 5 .
.It r
For tables which are referenced (used) by rules.
.It h
This flag is set when a table in the main ruleset is hidden by one or more
tables of the same name in sub-rulesets (anchors).
.El
.It Fl v
Produce more verbose output.
A second use of
.Fl v
will produce even more verbose output including ruleset warnings.
See previous section for its effect on table commands.
.It Fl x Ar level
Set the debug
.Ar level
(may be abbreviated) to one of the following:
.Pp
.Bl -tag -width xxxxxxxxxxxx -compact
.It Fl x Ar none
Don't generate debug messages.
.It Fl x Ar urgent
Generate debug messages only for serious errors.
.It Fl x Ar misc
Generate debug messages for various errors.
.It Fl x Ar loud
Generate debug messages for common conditions.
.El
.It Fl z
Clear per-rule statistics.
.El
.Sh FILES
.Bl -tag -width "/etc/pf.conf" -compact
.It Pa /etc/pf.conf
Packet filter rules file.
.El
.Sh SEE ALSO
.Xr pf 4 ,
.Xr pf.conf 5 ,
.Xr pf.os 5 ,
.Xr sysctl.conf 5 ,
.Xr ftp-proxy 8 ,
.Xr rc 8 ,
.Xr rc.conf 8 ,
.Xr sysctl 8
.Sh HISTORY
The
.Nm
program and the
.Xr pf 4
filter mechanism first appeared in
.Ox 3.0 .

1626
contrib/pf/pfctl/pfctl.c Normal file

File diff suppressed because it is too large Load Diff

115
contrib/pf/pfctl/pfctl.h Normal file
View File

@ -0,0 +1,115 @@
/* $OpenBSD: pfctl.h,v 1.25 2003/08/29 21:47:36 cedric Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _PFCTL_H_
#define _PFCTL_H_
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS, PFRB_MAX };
struct pfr_buffer {
int pfrb_type; /* type of content, see enum above */
int pfrb_size; /* number of objects in buffer */
int pfrb_msize; /* maximum number of objects in buffer */
void *pfrb_caddr; /* malloc'ated memory area */
};
#define PFRB_FOREACH(var, buf) \
for ((var) = pfr_buf_next((buf), NULL); \
(var) != NULL; \
(var) = pfr_buf_next((buf), (var)))
void pfr_set_fd(int);
int pfr_get_fd(void);
int pfr_clr_tables(struct pfr_table *, int *, int);
int pfr_add_tables(struct pfr_table *, int, int *, int);
int pfr_del_tables(struct pfr_table *, int, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
int pfr_clr_tstats(struct pfr_table *, int, int *, int);
int pfr_clr_addrs(struct pfr_table *, int *, int);
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
int *, int *, int *, int);
int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int);
int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int);
int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_set_tflags(struct pfr_table *, int, int, int, int *, int *, int);
int pfr_ina_begin(struct pfr_table *, int *, int *, int);
int pfr_ina_commit(struct pfr_table *, int, int *, int *, int);
int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *,
int *, int, int);
void pfr_buf_clear(struct pfr_buffer *);
int pfr_buf_add(struct pfr_buffer *, const void *);
void *pfr_buf_next(struct pfr_buffer *, const void *);
int pfr_buf_grow(struct pfr_buffer *, int);
int pfr_buf_load(struct pfr_buffer *, char *, int,
int (*)(struct pfr_buffer *, char *, int));
char *pfr_strerror(int);
int 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);
void warn_namespace_collision(const char *);
#ifndef DEFAULT_PRIORITY
#define DEFAULT_PRIORITY 1
#endif
#ifndef DEFAULT_QLIMIT
#define DEFAULT_QLIMIT 50
#endif
/*
* generalized service curve used for admission control
*/
struct segment {
LIST_ENTRY(segment) _next;
double x, y, d, m;
};
int check_commit_altq(int, int);
void pfaltq_store(struct pf_altq *);
void pfaltq_free(struct pf_altq *);
struct pf_altq *pfaltq_lookup(const char *);
char *rate2str(double);
void print_addr(struct pf_addr_wrap *, sa_family_t, int);
void print_host(struct pf_state_host *, sa_family_t, int);
void print_seq(struct pf_state_peer *);
void print_state(struct pf_state *, int);
int unmask(struct pf_addr *, sa_family_t);
int pfctl_cmdline_symset(char *);
#endif /* _PFCTL_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
/* $OpenBSD: pfctl_parser.h,v 1.67 2003/08/21 19:12:09 frantzen Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _PFCTL_PARSER_H_
#define _PFCTL_PARSER_H_
#define PF_OSFP_FILE "/etc/pf.os"
#define PF_OPT_DISABLE 0x0001
#define PF_OPT_ENABLE 0x0002
#define PF_OPT_VERBOSE 0x0004
#define PF_OPT_NOACTION 0x0008
#define PF_OPT_QUIET 0x0010
#define PF_OPT_CLRRULECTRS 0x0020
#define PF_OPT_USEDNS 0x0040
#define PF_OPT_VERBOSE2 0x0080
#define PF_OPT_DUMMYACTION 0x0100
#define PF_OPT_DEBUG 0x0200
#define PF_TH_ALL 0xFF
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
#define FCNT_NAMES { \
"searches", \
"inserts", \
"removals", \
NULL \
}
struct pfctl {
int dev;
int opts;
int loadopt;
u_int32_t tticket; /* table ticket */
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;
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;
u_int ifa_flags;
struct node_if *next;
struct node_if *tail;
};
struct node_host {
struct pf_addr_wrap addr;
struct pf_addr bcast;
sa_family_t af;
u_int8_t not;
u_int32_t ifindex; /* link-local IPv6 addrs */
char *ifname;
u_int ifa_flags;
struct node_host *next;
struct node_host *tail;
};
struct node_os {
char *os;
pf_osfp_t fingerprint;
struct node_os *next;
struct node_os *tail;
};
struct node_queue_bw {
u_int32_t bw_absolute;
u_int16_t bw_percent;
};
struct node_hfsc_sc {
struct node_queue_bw m1; /* slope of 1st segment; bps */
u_int d; /* x-projection of m1; msec */
struct node_queue_bw m2; /* slope of 2nd segment; bps */
u_int8_t used;
};
struct node_hfsc_opts {
struct node_hfsc_sc realtime;
struct node_hfsc_sc linkshare;
struct node_hfsc_sc upperlimit;
int flags;
};
struct node_queue_opt {
int qtype;
union {
struct cbq_opts cbq_opts;
struct priq_opts priq_opts;
struct node_hfsc_opts hfsc_opts;
} data;
};
SIMPLEQ_HEAD(node_tinithead, node_tinit);
struct node_tinit { /* table initializer */
SIMPLEQ_ENTRY(node_tinit) entries;
struct node_host *host;
char *file;
};
struct pfr_buffer; /* forward definition */
int pfctl_rules(int, char *, int, char *, char *);
int pfctl_add_rule(struct pfctl *, struct pf_rule *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t);
void pfctl_clear_pool(struct pf_pool *);
int pfctl_set_timeout(struct pfctl *, const char *, int, int);
int pfctl_set_optimization(struct pfctl *, const char *);
int pfctl_set_limit(struct pfctl *, const char *, unsigned int);
int pfctl_set_logif(struct pfctl *, char *);
int parse_rules(FILE *, struct pfctl *);
int parse_flags(char *);
int pfctl_load_anchors(int, int);
void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
void print_rule(struct pf_rule *, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
void print_status(struct pf_status *);
int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
struct node_queue_opt *);
int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
struct node_queue_opt *);
void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *,
struct node_queue_opt *);
void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *,
int, struct node_queue_opt *);
int pfctl_define_table(char *, int, int, const char *, const char *,
struct pfr_buffer *, u_int32_t);
void pfctl_clear_fingerprints(int, int);
int pfctl_file_fingerprints(int, int, const char *);
pf_osfp_t pfctl_get_fingerprint(const char *);
int pfctl_load_fingerprints(int, int);
char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t);
void pfctl_show_fingerprints(int);
struct icmptypeent {
const char *name;
u_int8_t type;
};
struct icmpcodeent {
const char *name;
u_int8_t type;
u_int8_t code;
};
const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t);
const struct icmptypeent *geticmptypebyname(char *, u_int8_t);
const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t);
const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t);
struct pf_timeout {
const char *name;
int timeout;
};
#define PFCTL_FLAG_FILTER 0x02
#define PFCTL_FLAG_NAT 0x04
#define PFCTL_FLAG_OPTION 0x08
#define PFCTL_FLAG_ALTQ 0x10
#define PFCTL_FLAG_TABLE 0x20
extern const struct pf_timeout pf_timeouts[];
void set_ipmask(struct node_host *, u_int8_t);
int check_netmask(struct node_host *, sa_family_t);
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 *host(const char *);
int append_addr(struct pfr_buffer *, char *, int);
int append_addr_host(struct pfr_buffer *,
struct node_host *, int, int);
#endif /* _PFCTL_PARSER_H_ */

View File

@ -0,0 +1,401 @@
/* $OpenBSD: pfctl_qstats.c,v 1.24 2003/07/31 09:46:08 kjc Exp $ */
/*
* Copyright (c) Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <altq/altq.h>
#include <altq/altq_cbq.h>
#include <altq/altq_priq.h>
#include <altq/altq_hfsc.h>
#include "pfctl.h"
#include "pfctl_parser.h"
union class_stats {
class_stats_t cbq_stats;
struct priq_classstats priq_stats;
struct hfsc_classstats hfsc_stats;
};
#define AVGN_MAX 8
#define STAT_INTERVAL 5
struct queue_stats {
union class_stats data;
int avgn;
double avg_bytes;
double avg_packets;
u_int64_t prev_bytes;
u_int64_t prev_packets;
};
struct pf_altq_node {
struct pf_altq altq;
struct pf_altq_node *next;
struct pf_altq_node *children;
struct queue_stats qstats;
};
int pfctl_update_qstats(int, struct pf_altq_node **);
void pfctl_insert_altq_node(struct pf_altq_node **,
const struct pf_altq, const struct queue_stats);
struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *,
const char *, const char *);
void pfctl_print_altq_node(int, const struct pf_altq_node *,
unsigned, int);
void print_cbqstats(struct queue_stats);
void print_priqstats(struct queue_stats);
void print_hfscstats(struct queue_stats);
void pfctl_free_altq_node(struct pf_altq_node *);
void pfctl_print_altq_nodestat(int,
const struct pf_altq_node *);
void update_avg(struct pf_altq_node *);
int
pfctl_show_altq(int dev, int opts, int verbose2)
{
struct pf_altq_node *root = NULL, *node;
if (pfctl_update_qstats(dev, &root))
return (-1);
for (node = root; node != NULL; node = node->next)
pfctl_print_altq_node(dev, node, 0, opts);
while (verbose2) {
printf("\n");
fflush(stdout);
sleep(STAT_INTERVAL);
if (pfctl_update_qstats(dev, &root))
return (-1);
for (node = root; node != NULL; node = node->next)
pfctl_print_altq_node(dev, node, 0, opts);
}
pfctl_free_altq_node(root);
return (0);
}
int
pfctl_update_qstats(int dev, struct pf_altq_node **root)
{
struct pf_altq_node *node;
struct pfioc_altq pa;
struct pfioc_qstats pq;
u_int32_t mnr, nr;
struct queue_stats qstats;
static u_int32_t last_ticket;
memset(&pa, 0, sizeof(pa));
memset(&pq, 0, sizeof(pq));
memset(&qstats, 0, sizeof(qstats));
if (ioctl(dev, DIOCGETALTQS, &pa)) {
warn("DIOCGETALTQS");
return (-1);
}
/* if a new set is found, start over */
if (pa.ticket != last_ticket && *root != NULL) {
pfctl_free_altq_node(*root);
*root = NULL;
}
last_ticket = pa.ticket;
mnr = pa.nr;
for (nr = 0; nr < mnr; ++nr) {
pa.nr = nr;
if (ioctl(dev, DIOCGETALTQ, &pa)) {
warn("DIOCGETALTQ");
return (-1);
}
if (pa.altq.qid > 0) {
pq.nr = nr;
pq.ticket = pa.ticket;
pq.buf = &qstats.data;
pq.nbytes = sizeof(qstats.data);
if (ioctl(dev, DIOCGETQSTATS, &pq)) {
warn("DIOCGETQSTATS");
return (-1);
}
if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
pa.altq.ifname)) != NULL) {
memcpy(&node->qstats.data, &qstats.data,
sizeof(qstats.data));
update_avg(node);
} else {
pfctl_insert_altq_node(root, pa.altq, qstats);
}
}
}
return (0);
}
void
pfctl_insert_altq_node(struct pf_altq_node **root,
const struct pf_altq altq, const struct queue_stats qstats)
{
struct pf_altq_node *node;
node = calloc(1, sizeof(struct pf_altq_node));
if (node == NULL)
err(1, "pfctl_insert_altq_node: calloc");
memcpy(&node->altq, &altq, sizeof(struct pf_altq));
memcpy(&node->qstats, &qstats, sizeof(qstats));
node->next = node->children = NULL;
if (*root == NULL)
*root = node;
else if (!altq.parent[0]) {
struct pf_altq_node *prev = *root;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
} else {
struct pf_altq_node *parent;
parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
if (parent == NULL)
errx(1, "parent %s not found", altq.parent);
if (parent->children == NULL)
parent->children = node;
else {
struct pf_altq_node *prev = parent->children;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
}
}
update_avg(node);
}
struct pf_altq_node *
pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
const char *ifname)
{
struct pf_altq_node *node, *child;
for (node = root; node != NULL; node = node->next) {
if (!strcmp(node->altq.qname, qname)
&& !(strcmp(node->altq.ifname, ifname)))
return (node);
if (node->children != NULL) {
child = pfctl_find_altq_node(node->children, qname,
ifname);
if (child != NULL)
return (child);
}
}
return (NULL);
}
void
pfctl_print_altq_node(int dev, const struct pf_altq_node *node, unsigned level,
int opts)
{
const struct pf_altq_node *child;
if (node == NULL)
return;
print_altq(&node->altq, level, NULL, NULL);
if (node->children != NULL) {
printf("{");
for (child = node->children; child != NULL;
child = child->next) {
printf("%s", child->altq.qname);
if (child->next != NULL)
printf(", ");
}
printf("}");
}
printf("\n");
if (opts & PF_OPT_VERBOSE)
pfctl_print_altq_nodestat(dev, node);
if (opts & PF_OPT_DEBUG)
printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n", node->altq.qid,
node->altq.ifname, rate2str((double)(node->altq.ifbandwidth)));
for (child = node->children; child != NULL;
child = child->next)
pfctl_print_altq_node(dev, child, level+1, opts);
}
void
pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
{
if (a->altq.qid == 0)
return;
switch (a->altq.scheduler) {
case ALTQT_CBQ:
print_cbqstats(a->qstats);
break;
case ALTQT_PRIQ:
print_priqstats(a->qstats);
break;
case ALTQT_HFSC:
print_hfscstats(a->qstats);
break;
}
}
void
print_cbqstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
cur.data.cbq_stats.xmit_cnt.packets,
cur.data.cbq_stats.xmit_cnt.bytes,
cur.data.cbq_stats.drop_cnt.packets,
cur.data.cbq_stats.drop_cnt.bytes);
printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n",
cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
print_priqstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
cur.data.priq_stats.xmitcnt.packets,
cur.data.priq_stats.xmitcnt.bytes,
cur.data.priq_stats.dropcnt.packets,
cur.data.priq_stats.dropcnt.bytes);
printf(" [ qlength: %3d/%3d ]\n",
cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
print_hfscstats(struct queue_stats cur)
{
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
cur.data.hfsc_stats.xmit_cnt.packets,
cur.data.hfsc_stats.xmit_cnt.bytes,
cur.data.hfsc_stats.drop_cnt.packets,
cur.data.hfsc_stats.drop_cnt.bytes);
printf(" [ qlength: %3d/%3d ]\n",
cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
if (cur.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
cur.avg_packets / STAT_INTERVAL,
rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
}
void
pfctl_free_altq_node(struct pf_altq_node *node)
{
while (node != NULL) {
struct pf_altq_node *prev;
if (node->children != NULL)
pfctl_free_altq_node(node->children);
prev = node;
node = node->next;
free(prev);
}
}
void
update_avg(struct pf_altq_node *a)
{
struct queue_stats *qs;
u_int64_t b, p;
int n;
if (a->altq.qid == 0)
return;
qs = &a->qstats;
n = qs->avgn;
switch (a->altq.scheduler) {
case ALTQT_CBQ:
b = qs->data.cbq_stats.xmit_cnt.bytes;
p = qs->data.cbq_stats.xmit_cnt.packets;
break;
case ALTQT_PRIQ:
b = qs->data.priq_stats.xmitcnt.bytes;
p = qs->data.priq_stats.xmitcnt.packets;
break;
case ALTQT_HFSC:
b = qs->data.hfsc_stats.xmit_cnt.bytes;
p = qs->data.hfsc_stats.xmit_cnt.packets;
break;
default:
b = 0;
p = 0;
break;
}
if (n == 0) {
qs->prev_bytes = b;
qs->prev_packets = p;
qs->avgn++;
return;
}
if (b >= qs->prev_bytes)
qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
(b - qs->prev_bytes)) / n;
if (p >= qs->prev_packets)
qs->avg_packets = ((qs->avg_packets * (n - 1)) +
(p - qs->prev_packets)) / n;
qs->prev_bytes = b;
qs->prev_packets = p;
if (n < AVGN_MAX)
qs->avgn++;
}

View File

@ -0,0 +1,639 @@
/* $OpenBSD: pfctl_radix.c,v 1.21 2003/09/24 09:12:35 cedric Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <err.h>
#include "pfctl.h"
#define BUF_SIZE 256
extern int dev;
static int pfr_next_token(char buf[], FILE *);
int
pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
{
struct pfioc_table io;
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
if (ioctl(dev, DIOCRCLRTABLES, &io))
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRADDTABLES, &io))
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
return (0);
}
int
pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRDELTABLES, &io))
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
int flags)
{
struct pfioc_table io;
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETTABLES, &io))
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
int flags)
{
struct pfioc_table io;
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETTSTATS, &io))
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
{
struct pfioc_table io;
if (tbl == NULL) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
if (ioctl(dev, DIOCRCLRADDRS, &io))
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRADDADDRS, &io))
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
return (0);
}
int
pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *ndel, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRDELADDRS, &io))
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *size2, int *nadd, int *ndel, int *nchange, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
if (ioctl(dev, DIOCRSETADDRS, &io))
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
if (ndel != NULL)
*ndel = io.pfrio_ndel;
if (nchange != NULL)
*nchange = io.pfrio_nchange;
if (size2 != NULL)
*size2 = io.pfrio_size2;
return (0);
}
int
pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
int flags)
{
struct pfioc_table io;
if (tbl == NULL || size == NULL || *size < 0 || (*size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETADDRS, &io))
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
int flags)
{
struct pfioc_table io;
if (tbl == NULL || size == NULL || *size < 0 || (*size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETASTATS, &io))
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nzero, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRCLRASTATS, &io))
return (-1);
if (nzero != NULL)
*nzero = io.pfrio_nzero;
return (0);
}
int
pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && !tbl)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRCLRTSTATS, &io))
return (-1);
if (nzero)
*nzero = io.pfrio_nzero;
return (0);
}
int
pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag,
int *nchange, int *ndel, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && !tbl)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
io.pfrio_setflag = setflag;
io.pfrio_clrflag = clrflag;
if (ioctl(dev, DIOCRSETTFLAGS, &io))
return (-1);
if (nchange)
*nchange = io.pfrio_nchange;
if (ndel)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nmatch, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRTSTADDRS, &io))
return (-1);
if (nmatch)
*nmatch = io.pfrio_nmatch;
return (0);
}
int
pfr_ina_begin(struct pfr_table *trs, int *ticket, int *ndel, int flags)
{
struct pfioc_table io;
bzero(&io, sizeof io);
if (trs != NULL)
io.pfrio_table = *trs;
io.pfrio_flags = flags;
if (ioctl(dev, DIOCRINABEGIN, &io))
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
if (ticket != NULL)
*ticket = io.pfrio_ticket;
return (0);
}
int
pfr_ina_commit(struct pfr_table *trs, int ticket, int *nadd, int *nchange,
int flags)
{
struct pfioc_table io;
bzero(&io, sizeof io);
if (trs != NULL)
io.pfrio_table = *trs;
io.pfrio_flags = flags;
io.pfrio_ticket = ticket;
if (ioctl(dev, DIOCRINACOMMIT, &io))
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
if (nchange != NULL)
*nchange = io.pfrio_nchange;
return (0);
}
int
pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int *naddr, int ticket, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
io.pfrio_ticket = ticket;
if (ioctl(dev, DIOCRINADEFINE, &io))
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
if (naddr != NULL)
*naddr = io.pfrio_naddr;
return (0);
}
/* buffer management code */
size_t buf_esize[PFRB_MAX] = { 0,
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
};
/*
* add one element to the buffer
*/
int
pfr_buf_add(struct pfr_buffer *b, const void *e)
{
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
e == NULL) {
errno = EINVAL;
return (-1);
}
bs = buf_esize[b->pfrb_type];
if (b->pfrb_size == b->pfrb_msize)
if (pfr_buf_grow(b, 0))
return (-1);
memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
b->pfrb_size++;
return (0);
}
/*
* return next element of the buffer (or first one if prev is NULL)
* see PFRB_FOREACH macro
*/
void *
pfr_buf_next(struct pfr_buffer *b, const void *prev)
{
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
return (NULL);
if (b->pfrb_size == 0)
return (NULL);
if (prev == NULL)
return (b->pfrb_caddr);
bs = buf_esize[b->pfrb_type];
if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
return (NULL);
return (((caddr_t)prev) + bs);
}
/*
* minsize:
* 0: make the buffer somewhat bigger
* n: make room for "n" entries in the buffer
*/
int
pfr_buf_grow(struct pfr_buffer *b, int minsize)
{
caddr_t p;
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
errno = EINVAL;
return (-1);
}
if (minsize != 0 && minsize <= b->pfrb_msize)
return (0);
bs = buf_esize[b->pfrb_type];
if (!b->pfrb_msize) {
if (minsize < 64)
minsize = 64;
b->pfrb_caddr = calloc(bs, minsize);
if (b->pfrb_caddr == NULL)
return (-1);
b->pfrb_msize = minsize;
} else {
if (minsize == 0)
minsize = b->pfrb_msize * 2;
if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
/* msize overflow */
errno = ENOMEM;
return (-1);
}
p = realloc(b->pfrb_caddr, minsize * bs);
if (p == NULL)
return (-1);
bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
b->pfrb_caddr = p;
b->pfrb_msize = minsize;
}
return (0);
}
/*
* reset buffer and free memory.
*/
void
pfr_buf_clear(struct pfr_buffer *b)
{
if (b == NULL)
return;
if (b->pfrb_caddr != NULL)
free(b->pfrb_caddr);
b->pfrb_caddr = NULL;
b->pfrb_size = b->pfrb_msize = 0;
}
int
pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
int (*append_addr)(struct pfr_buffer *, char *, int))
{
FILE *fp;
char buf[BUF_SIZE];
int rv;
if (file == NULL)
return (0);
if (!strcmp(file, "-"))
fp = stdin;
else {
fp = fopen(file, "r");
if (fp == NULL)
return (-1);
}
while ((rv = pfr_next_token(buf, fp)) == 1)
if (append_addr(b, buf, nonetwork)) {
rv = -1;
break;
}
if (fp != stdin)
fclose(fp);
return (rv);
}
int
pfr_next_token(char buf[BUF_SIZE], FILE *fp)
{
static char next_ch = ' ';
int i = 0;
for (;;) {
/* skip spaces */
while (isspace(next_ch) && !feof(fp))
next_ch = fgetc(fp);
/* remove from '#' until end of line */
if (next_ch == '#')
while (!feof(fp)) {
next_ch = fgetc(fp);
if (next_ch == '\n')
break;
}
else
break;
}
if (feof(fp)) {
next_ch = ' ';
return (0);
}
do {
if (i < BUF_SIZE)
buf[i++] = next_ch;
next_ch = fgetc(fp);
} while (!feof(fp) && !isspace(next_ch));
if (i >= BUF_SIZE) {
errno = EINVAL;
return (-1);
}
buf[i] = '\0';
return (1);
}
char *
pfr_strerror(int errnum)
{
switch (errnum) {
case ESRCH:
return "Table does not exist";
case ENOENT:
return "Anchor or Ruleset does not exist";
default:
return strerror(errnum);
}
}

View File

@ -0,0 +1,524 @@
/* $OpenBSD: pfctl_table.c,v 1.50 2003/08/29 21:47:36 cedric Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "pfctl_parser.h"
#include "pfctl.h"
extern void usage(void);
static int pfctl_table(int, char *[], char *, const char *, char *,
const char *, const char *, int);
static void print_table(struct pfr_table *, int, int);
static void print_tstats(struct pfr_tstats *, int);
static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
static void print_astats(struct pfr_astats *, int);
static void radix_perror(void);
static void xprintf(int, const char *, ...);
static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
{ "In/Block:", "In/Pass:", "In/XPass:" },
{ "Out/Block:", "Out/Pass:", "Out/XPass:" }
};
#define RVTEST(fct) do { \
if ((!(opts & PF_OPT_NOACTION) || \
(opts & PF_OPT_DUMMYACTION)) && \
(fct)) { \
radix_perror(); \
goto _error; \
} \
} while (0)
#define CREATE_TABLE do { \
table.pfrt_flags |= PFR_TFLAG_PERSIST; \
RVTEST(pfr_add_tables(&table, 1, &nadd, flags)); \
if (nadd) { \
warn_namespace_collision(table.pfrt_name); \
xprintf(opts, "%d table created", nadd); \
if (opts & PF_OPT_NOACTION) \
return (0); \
} \
table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
} while(0)
int
pfctl_clear_tables(const char *anchor, const char *ruleset, int opts)
{
return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, ruleset, opts);
}
int
pfctl_show_tables(const char *anchor, const char *ruleset, int opts)
{
return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, ruleset, opts);
}
int
pfctl_command_tables(int argc, char *argv[], char *tname,
const char *command, char *file, const char *anchor, const char *ruleset,
int opts)
{
if (tname == NULL || command == NULL)
usage();
return pfctl_table(argc, argv, tname, command, file, anchor, ruleset,
opts);
}
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;
if (command == NULL)
usage();
if (opts & PF_OPT_NOACTION)
flags |= PFR_FLAG_DUMMY;
bzero(&b, sizeof(b));
bzero(&b2, sizeof(b2));
bzero(&table, sizeof(table));
if (tname != NULL) {
if (strlen(tname) >= PF_TABLE_NAME_SIZE)
usage();
if (strlcpy(table.pfrt_name, tname,
sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
errx(1, "pfctl_table: strlcpy");
}
if (strlcpy(table.pfrt_anchor, anchor,
sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor) ||
strlcpy(table.pfrt_ruleset, ruleset,
sizeof(table.pfrt_ruleset)) >= sizeof(table.pfrt_ruleset))
errx(1, "pfctl_table: strlcpy");
if (!strcmp(command, "-F")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_clr_tables(&table, &ndel, flags));
xprintf(opts, "%d tables deleted", ndel);
} else if (!strcmp(command, "-s")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
PFRB_TSTATS : PFRB_TABLES;
if (argc || file != NULL)
usage();
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (opts & PF_OPT_VERBOSE2)
RVTEST(pfr_get_tstats(&table,
b.pfrb_caddr, &b.pfrb_size, flags));
else
RVTEST(pfr_get_tables(&table,
b.pfrb_caddr, &b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(p, &b)
if (opts & PF_OPT_VERBOSE2)
print_tstats(p, opts & PF_OPT_DEBUG);
else
print_table(p, opts & PF_OPT_VERBOSE,
opts & PF_OPT_DEBUG);
} else if (!strcmp(command, "kill")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
xprintf(opts, "%d table deleted", ndel);
} else if (!strcmp(command, "flush")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_clr_addrs(&table, &ndel, flags));
xprintf(opts, "%d addresses deleted", ndel);
} else if (!strcmp(command, "add")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&nadd, flags));
xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "delete")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0))
goto _error;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&ndel, flags));
xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "replace")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
for (;;) {
int sz2 = b.pfrb_msize;
RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&sz2, &nadd, &ndel, &nchange, flags));
if (sz2 <= b.pfrb_msize) {
b.pfrb_size = sz2;
break;
} else
pfr_buf_grow(&b, sz2);
}
if (nadd)
xprintf(opts, "%d addresses added", nadd);
if (ndel)
xprintf(opts, "%d addresses deleted", ndel);
if (nchange)
xprintf(opts, "%d addresses changed", nchange);
if (!nadd && !ndel && !nchange)
xprintf(opts, "no changes");
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "show")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
PFRB_ASTATS : PFRB_ADDRS;
if (argc || file != NULL)
usage();
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (opts & PF_OPT_VERBOSE)
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
else
RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(p, &b)
if (opts & PF_OPT_VERBOSE)
print_astats(p, opts & PF_OPT_USEDNS);
else
print_addrx(p, NULL, opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "test")) {
b.pfrb_type = PFRB_ADDRS;
b2.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 1))
goto _error;
if (opts & PF_OPT_VERBOSE2) {
flags |= PFR_FLAG_REPLACE;
PFRB_FOREACH(a, &b)
if (pfr_buf_add(&b2, a))
err(1, "duplicate buffer");
}
RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&nmatch, flags));
xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
if (opts & PF_OPT_VERBOSE && !(opts & PF_OPT_VERBOSE2))
PFRB_FOREACH(a, &b)
if (a->pfra_fback == PFR_FB_MATCH)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
if (opts & PF_OPT_VERBOSE2) {
a2 = NULL;
PFRB_FOREACH(a, &b) {
a2 = pfr_buf_next(&b2, a2);
print_addrx(a2, a, opts & PF_OPT_USEDNS);
}
}
if (nmatch < b.pfrb_size)
rv = 2;
} else if (!strcmp(command, "zero")) {
if (argc || file != NULL)
usage();
flags |= PFR_FLAG_ADDRSTOO;
RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
xprintf(opts, "%d table/stats cleared", nzero);
} else
warnx("pfctl_table: unknown command '%s'", command);
goto _cleanup;
_error:
rv = -1;
_cleanup:
pfr_buf_clear(&b);
pfr_buf_clear(&b2);
return (rv);
}
void
print_table(struct pfr_table *ta, int verbose, int debug)
{
if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
return;
if (verbose) {
printf("%c%c%c%c%c%c\t%s",
(ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
(ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
(ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
(ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
ta->pfrt_name);
if (ta->pfrt_anchor[0])
printf("\t%s", ta->pfrt_anchor);
if (ta->pfrt_ruleset[0])
printf(":%s", ta->pfrt_ruleset);
puts("");
} else
puts(ta->pfrt_name);
}
void
print_tstats(struct pfr_tstats *ts, int debug)
{
time_t time = ts->pfrts_tzero;
int dir, op;
if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
return;
print_table(&ts->pfrts_t, 1, debug);
printf("\tAddresses: %d\n", ts->pfrts_cnt);
printf("\tCleared: %s", ctime(&time));
printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
ts->pfrts_refcnt[PFR_REFCNT_RULE]);
printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
ts->pfrts_nomatch, ts->pfrts_match);
for (dir = 0; dir < PFR_DIR_MAX; dir++)
for (op = 0; op < PFR_OP_TABLE_MAX; op++)
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
stats_text[dir][op],
ts->pfrts_packets[dir][op],
ts->pfrts_bytes[dir][op]);
}
int
load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
int nonetwork)
{
while (argc--)
if (append_addr(b, *argv++, nonetwork)) {
if (errno)
warn("cannot decode %s", argv[-1]);
return (-1);
}
if (pfr_buf_load(b, file, nonetwork, append_addr)) {
warn("cannot load %s", file);
return (-1);
}
return (0);
}
void
print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
{
char ch, buf[256] = "{error}";
char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y' };
unsigned int fback, hostnet;
fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
if (ad->pfra_net < hostnet)
printf("/%d", ad->pfra_net);
if (rad != NULL && fback != PFR_FB_NONE) {
if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
errx(1, "print_addrx: strlcpy");
inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
if (rad->pfra_net < hostnet)
printf("/%d", rad->pfra_net);
}
if (rad != NULL && fback == PFR_FB_NONE)
printf("\t nomatch");
if (dns && ad->pfra_net == hostnet) {
char host[NI_MAXHOST];
union sockaddr_union sa;
strlcpy(host, "?", sizeof(host));
bzero(&sa, sizeof(sa));
sa.sa.sa_family = ad->pfra_af;
if (sa.sa.sa_family == AF_INET) {
sa.sa.sa_len = sizeof(sa.sin);
sa.sin.sin_addr = ad->pfra_ip4addr;
} else {
sa.sa.sa_len = sizeof(sa.sin6);
sa.sin6.sin6_addr = ad->pfra_ip6addr;
}
if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
NULL, 0, NI_NAMEREQD) == 0)
printf("\t(%s)", host);
}
printf("\n");
}
void
print_astats(struct pfr_astats *as, int dns)
{
time_t time = as->pfras_tzero;
int dir, op;
print_addrx(&as->pfras_a, NULL, dns);
printf("\tCleared: %s", ctime(&time));
for (dir = 0; dir < PFR_DIR_MAX; dir++)
for (op = 0; op < PFR_OP_ADDR_MAX; op++)
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
stats_text[dir][op],
as->pfras_packets[dir][op],
as->pfras_bytes[dir][op]);
}
void
radix_perror(void)
{
extern char *__progname;
fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
}
int
pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
const char *ruleset, struct pfr_buffer *ab, u_int32_t ticket)
{
struct pfr_table tbl;
bzero(&tbl, sizeof(tbl));
if (strlcpy(tbl.pfrt_name, name,
sizeof(tbl.pfrt_name)) >= sizeof(tbl.pfrt_name) ||
strlcpy(tbl.pfrt_anchor, anchor,
sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor) ||
strlcpy(tbl.pfrt_ruleset, ruleset,
sizeof(tbl.pfrt_ruleset)) >= sizeof(tbl.pfrt_ruleset))
errx(1, "pfctl_define_table: strlcpy");
tbl.pfrt_flags = flags;
return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
}
void
warn_namespace_collision(const char *filter)
{
struct pfr_buffer b;
struct pfr_table *t;
const char *name = NULL, *lastcoll;
int coll = 0;
bzero(&b, sizeof(b));
b.pfrb_type = PFRB_TABLES;
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (pfr_get_tables(NULL, b.pfrb_caddr,
&b.pfrb_size, PFR_FLAG_ALLRSETS))
err(1, "pfr_get_tables");
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(t, &b) {
if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
continue;
if (filter != NULL && strcmp(filter, t->pfrt_name))
continue;
if (!t->pfrt_anchor[0])
name = t->pfrt_name;
else if (name != NULL && !strcmp(name, t->pfrt_name)) {
coll++;
lastcoll = name;
name = NULL;
}
}
if (coll == 1)
warnx("warning: namespace collision with <%s> global table.",
lastcoll);
else if (coll > 1)
warnx("warning: namespace collisions with %d global tables.",
coll);
pfr_buf_clear(&b);
}
void
xprintf(int opts, const char *fmt, ...)
{
va_list args;
if (opts & PF_OPT_QUIET)
return;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (opts & PF_OPT_DUMMYACTION)
fprintf(stderr, " (dummy).\n");
else if (opts & PF_OPT_NOACTION)
fprintf(stderr, " (syntax only).\n");
else
fprintf(stderr, ".\n");
}

179
contrib/pf/pflogd/pflogd.8 Normal file
View File

@ -0,0 +1,179 @@
.\" $OpenBSD: pflogd.8,v 1.22 2003/06/03 13:16:08 jmc Exp $
.\"
.\" Copyright (c) 2001 Can Erkin Acar. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd July 9, 2001
.Dt PFLOGD 8
.Os
.Sh NAME
.Nm pflogd
.Nd packet filter logging daemon
.Sh SYNOPSIS
.Nm pflogd
.Op Fl D
.Op Fl d Ar delay
.Op Fl f Ar filename
.Op Fl s Ar snaplen
.Op Ar expression
.Sh DESCRIPTION
.Nm
is a background daemon which reads packets logged by
.Xr pf 4
to the packet logging interface
.Pa pflog0
and writes the packets to a logfile (normally
.Pa /var/log/pflog )
in
.Xr tcpdump 8
binary format.
These logs can be reviewed later using the
.Fl r
option of
.Xr tcpdump 8 ,
hopefully offline in case there are bugs in the packet parsing code of
.Xr tcpdump 8 .
.Pp
.Nm
closes and then re-opens the log file when it receives
.Va SIGHUP ,
permitting
.Xr newsyslog 8
to rotate logfiles automatically.
.Va SIGALRM
causes
.Nm
to flush the current logfile buffers to the disk, thus making the most
recent logs available.
The buffers are also flushed every
.Ar delay
seconds.
.Pp
If the log file contains data after a restart or a
.Va SIGHUP ,
new logs are appended to the existing file.
If the existing log file was created with a different snaplen,
.Nm
temporarily uses the old snaplen to keep the log file consistent.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl d Ar delay
Time in seconds to delay between automatic flushes of the file.
This may be specified with a value between 5 and 3600 seconds.
If not specified, the default is 60 seconds.
.It Fl D
Debugging mode.
.Nm
does not disassociate from the controlling terminal.
.It Fl f Ar filename
Log output filename.
Default is
.Pa /var/log/pflog .
.It Fl s Ar snaplen
Analyze at most the first
.Ar snaplen
bytes of data from each packet rather than the default of 96.
The default of 96 is adequate for IP, ICMP, TCP, and UDP headers but may
truncate protocol information for other protocols.
Other file parsers may desire a higher snaplen.
.It Ar expression
Selects which packets will be dumped, using the regular language of
.Xr tcpdump 8 .
.El
.Sh FILES
.Bl -tag -width /var/run/pflogd.pid -compact
.It Pa /var/run/pflogd.pid
Process ID of the currently running
.Nm pflogd .
.It Pa /var/log/pflog
Default log file.
.El
.Sh EXAMPLES
Log specific tcp packets to a different log file with a large snaplen
(useful with a log-all rule to dump complete sessions)
.Bd -literal -offset indent
# pflogd -s 1600 -f suspicious.log port 80 and host evilhost
.Ed
.Pp
Display binary logs:
.Bd -literal -offset indent
# tcpdump -n -e -ttt -r /var/log/pflog
.Ed
.Pp
Display the logs in real time (this does not interfere with the
operation of pflogd):
.Bd -literal -offset indent
# tcpdump -n -e -ttt -i pflog0
.Ed
.Pp
Tcpdump has been extended to be able to filter on the pfloghdr
structure defined in
.Aq Ar net/if_pflog.h .
Tcpdump can restrict the output
to packets logged on a specified interface, a rule number, a reason,
a direction, an ip family or an action.
.Pp
.Bl -tag -width "reason match " -compact
.It ip
Address family equals IPv4.
.It ip6
Address family equals IPv6.
.It ifname kue0
Interface name equals "kue0"
.It on kue0
Interface name equals "kue0"
.It rulenum 10
Rule number equals 10.
.It reason match
Reason equals match.
Also accepts "bad-offset", "fragment", "short", "normalize" and "memory".
.It action pass
Action equals pass.
Also accepts "block".
.It inbound
The direction was inbound.
.It outbound
The direction was outbound.
.El
.Pp
Display the logs in real time of inbound packets that were blocked on
the wi0 interface:
.Bd -literal -offset indent
# tcpdump -n -e -ttt -i pflog0 inbound and action block and on wi0
.Ed
.Sh SEE ALSO
.Xr pcap 3 ,
.Xr pf 4 ,
.Xr pflog 4 ,
.Xr pf.conf 5 ,
.Xr newsyslog 8 ,
.Xr tcpdump 8
.Sh HISTORY
The
.Nm
command appeared in
.Ox 3.0 .
.Sh AUTHORS
Can Erkin Acar

400
contrib/pf/pflogd/pflogd.c Normal file
View File

@ -0,0 +1,400 @@
/* $OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $ */
/*
* Copyright (c) 2001 Theo de Raadt
* Copyright (c) 2001 Can Erkin Acar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pcap-int.h>
#include <pcap.h>
#include <syslog.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>
#include <fcntl.h>
#include <util.h>
#define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */
#define PCAP_TO_MS 500 /* pcap read timeout (ms) */
#define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */
#define PCAP_OPT_FIL 0 /* filter optimization */
#define FLUSH_DELAY 60 /* flush delay */
#define PFLOGD_LOG_FILE "/var/log/pflog"
#define PFLOGD_DEFAULT_IF "pflog0"
pcap_t *hpcap;
pcap_dumper_t *dpcap;
int Debug = 0;
int snaplen = DEF_SNAPLEN;
volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup;
char *filename = PFLOGD_LOG_FILE;
char *interface = PFLOGD_DEFAULT_IF;
char *filter = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
int log_debug = 0;
unsigned int delay = FLUSH_DELAY;
char *copy_argv(char * const *argv);
int init_pcap(void);
void logmsg(int priority, const char *message, ...);
int reset_dump(void);
void sig_alrm(int);
void sig_close(int);
void sig_hup(int);
void usage(void);
char *
copy_argv(char * const *argv)
{
size_t len = 0, n;
char *buf;
if (argv == NULL)
return (NULL);
for (n = 0; argv[n]; n++)
len += strlen(argv[n])+1;
if (len == 0)
return (NULL);
buf = malloc(len);
if (buf == NULL)
return (NULL);
strlcpy(buf, argv[0], len);
for (n = 1; argv[n]; n++) {
strlcat(buf, " ", len);
strlcat(buf, argv[n], len);
}
return (buf);
}
void
logmsg(int pri, const char *message, ...)
{
va_list ap;
va_start(ap, message);
if (log_debug) {
vfprintf(stderr, message, ap);
fprintf(stderr, "\n");
} else
vsyslog(pri, message, ap);
va_end(ap);
}
__dead void
usage(void)
{
fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] ");
fprintf(stderr, "[-s snaplen] [expression]\n");
exit(1);
}
void
sig_close(int sig)
{
gotsig_close = 1;
}
void
sig_hup(int sig)
{
gotsig_hup = 1;
}
void
sig_alrm(int sig)
{
gotsig_alrm = 1;
}
int
init_pcap(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);
if (pcap_datalink(hpcap) != DLT_PFLOG) {
logmsg(LOG_ERR, "Invalid datalink type");
pcap_close(hpcap);
hpcap = oldhpcap;
return (-1);
}
if (oldhpcap)
pcap_close(oldhpcap);
snaplen = pcap_snapshot(hpcap);
return (0);
}
int
reset_dump(void)
{
struct pcap_file_header hdr;
struct stat st;
int tmpsnap;
FILE *fp;
if (hpcap == NULL)
return (1);
if (dpcap) {
pcap_dump_close(dpcap);
dpcap = 0;
}
/*
* Basically reimplement pcap_dump_open() because it truncates
* files and duplicates headers and such.
*/
fp = fopen(filename, "a+");
if (fp == NULL) {
snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
filename, pcap_strerror(errno));
logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap));
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));
return (1);
}
dpcap = (pcap_dumper_t *)fp;
#define TCPDUMP_MAGIC 0xa1b2c3d4
if (st.st_size == 0) {
if (snaplen != pcap_snapshot(hpcap)) {
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");
}
}
hdr.magic = TCPDUMP_MAGIC;
hdr.version_major = PCAP_VERSION_MAJOR;
hdr.version_minor = PCAP_VERSION_MINOR;
hdr.thiszone = hpcap->tzoff;
hdr.snaplen = hpcap->snapshot;
hdr.sigfigs = 0;
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) {
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;
}
}
(void) fseek(fp, 0L, SEEK_END);
return (0);
}
int
main(int argc, char **argv)
{
struct pcap_stat pstat;
int ch, np;
while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) {
switch (ch) {
case 'D':
Debug = 1;
break;
case 'd':
delay = atoi(optarg);
if (delay < 5 || delay > 60*60)
usage();
break;
case 'f':
filename = optarg;
break;
case 's':
snaplen = atoi(optarg);
if (snaplen <= 0)
snaplen = DEF_SNAPLEN;
break;
default:
usage();
}
}
log_debug = Debug;
argc -= optind;
argv += optind;
if (!Debug) {
openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
if (daemon(0, 0)) {
logmsg(LOG_WARNING, "Failed to become daemon: %s",
strerror(errno));
}
pidfile(NULL);
}
(void)umask(S_IRWXG | S_IRWXO);
signal(SIGTERM, sig_close);
signal(SIGINT, sig_close);
signal(SIGQUIT, sig_close);
signal(SIGALRM, sig_alrm);
signal(SIGHUP, sig_hup);
alarm(delay);
if (argc) {
filter = copy_argv(argv);
if (filter == NULL)
logmsg(LOG_NOTICE, "Failed to form filter expression");
}
if (init_pcap()) {
logmsg(LOG_ERR, "Exiting, init failure");
exit(1);
}
if (reset_dump()) {
logmsg(LOG_ERR, "Failed to open log file %s", filename);
pcap_close(hpcap);
exit(1);
}
while (1) {
np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap);
if (np < 0)
logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
if (gotsig_close)
break;
if (gotsig_hup) {
if (reset_dump()) {
logmsg(LOG_ERR, "Failed to open log file!");
break;
}
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;
}
}
gotsig_alrm = 0;
alarm(delay);
}
}
logmsg(LOG_NOTICE, "Exiting due to signal");
if (dpcap)
pcap_dump_close(dpcap);
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);
pcap_close(hpcap);
if (!Debug)
closelog();
return (0);
}