Import pf userland from OpenBSD 4.1 and (for ftp-proxy) libevent 1.3b as

a local lib.
This commit is contained in:
Max Laier 2007-07-03 12:22:02 +00:00
parent 61a1372b41
commit 67ecd4f3a4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/pf/dist/; revision=171169
43 changed files with 8828 additions and 2733 deletions

View File

@ -1,28 +1,18 @@
.\" $OpenBSD: authpf.8,v 1.38 2005/01/04 09:57:04 jmc Exp $
.\" $OpenBSD: authpf.8,v 1.43 2007/02/24 17:21:04 beck Exp $
.\"
.\" Copyright (c) 2002 Bob Beck (beck@openbsd.org>. All rights reserved.
.\" Copyright (c) 1998-2007 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.
.\" 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.
.\"
.\" 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.
.\" 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 January 10, 2002
.Dt AUTHPF 8
@ -225,8 +215,11 @@ 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.
Also note that
Also note that the various SSH tunnelling mechanisms,
such as
.Ar AllowTcpForwarding
and
.Ar PermitTunnel ,
should be disabled for
.Nm
users to prevent them from circumventing restrictions imposed by the
@ -424,8 +417,7 @@ TCP connections.
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 log quick on $internal_if proto tcp from $user_ip to any
pass in quick on $internal_if from $user_ip to any
.Ed
.Pp
@ -440,16 +432,15 @@ 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
-> 127.0.0.1 port 8021
# 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
port { 21, 22, 80, 443 }
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 udp from $user_ip to $ipsec_gw port = isakmp
pass in quick proto esp from $user_ip to $ipsec_gw
.Ed
.Pp
@ -464,7 +455,7 @@ int_if = "fxp0"
# nat and tag connections...
nat on $ext_if from $user_ip to any tag $user_ip -> $ext_addr
pass in quick on $int_if from $user_ip to any
pass out log quick on $ext_if tagged $user_ip keep state
pass out log quick on $ext_if tagged $user_ip
.Ed
.Pp
With the above rules added by
@ -490,7 +481,7 @@ lines will give SMTP and IMAP access to logged in users:
.Bd -literal
table <authpf_users> persist
pass in on $ext_if proto tcp from <authpf_users> \e
to port { smtp imap } keep state
to port { smtp imap }
.Ed
.Pp
It is also possible to use the "authpf_users"
@ -516,6 +507,7 @@ rdr-anchor "authpf/*" from <authpf_users>
.Sh SEE ALSO
.Xr pf 4 ,
.Xr pf.conf 5 ,
.Xr securelevel 7 ,
.Xr ftp-proxy 8
.Sh HISTORY
The

View File

@ -1,28 +1,19 @@
/* $OpenBSD: authpf.c,v 1.89 2005/02/10 04:24:15 joel Exp $ */
/* $OpenBSD: authpf.c,v 1.104 2007/02/24 17:35:08 beck Exp $ */
/*
* Copyright (C) 1998 - 2002 Bob Beck (beck@openbsd.org).
* Copyright (C) 1998 - 2007 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* THE 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>
@ -50,15 +41,13 @@
#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 int change_table(int, const char *, const char *);
static int change_table(int, const char *);
static void authpf_kill_states(void);
int dev; /* pf device */
@ -67,7 +56,6 @@ char rulesetname[MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 2];
char tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
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. */
@ -92,11 +80,16 @@ main(int argc, char *argv[])
struct in6_addr ina;
struct passwd *pw;
char *cp;
gid_t gid;
uid_t uid;
char *shell;
login_cap_t *lc;
config = fopen(PATH_CONFFILE, "r");
if (config == NULL) {
syslog(LOG_ERR, "can not open %s (%m)", PATH_CONFFILE);
exit(1);
}
if ((cp = getenv("SSH_TTY")) == NULL) {
syslog(LOG_ERR, "non-interactive session connection for authpf");
@ -133,7 +126,6 @@ main(int argc, char *argv[])
uid = getuid();
pw = getpwuid(uid);
endpwent();
if (pw == NULL) {
syslog(LOG_ERR, "cannot find user for uid %u", uid);
goto die;
@ -246,6 +238,8 @@ main(int argc, char *argv[])
if (++lockcnt > 10) {
syslog(LOG_ERR, "cannot kill previous authpf (pid %d)",
otherpid);
fclose(pidfp);
pidfp = NULL;
goto dogdeath;
}
sleep(1);
@ -255,12 +249,22 @@ main(int argc, char *argv[])
* it's lock, giving us a chance to get it now
*/
fclose(pidfp);
pidfp = NULL;
} while (1);
/* whack the group list */
gid = getegid();
if (setgroups(1, &gid) == -1) {
syslog(LOG_INFO, "setgroups: %s", strerror(errno));
do_death(0);
}
/* revoke privs */
seteuid(getuid());
setuid(getuid());
uid = getuid();
if (setresuid(uid, uid, uid) == -1) {
syslog(LOG_INFO, "setresuid: %s", strerror(errno));
do_death(0);
}
openlog("authpf", LOG_PID | LOG_NDELAY, LOG_DAEMON);
if (!check_luser(PATH_BAN_DIR, luser) || !allowed_luser(luser)) {
@ -268,8 +272,8 @@ main(int argc, char *argv[])
do_death(0);
}
if (config == NULL || read_config(config)) {
syslog(LOG_INFO, "bad or nonexistent %s", PATH_CONFFILE);
if (read_config(config)) {
syslog(LOG_ERR, "invalid config file %s", PATH_CONFFILE);
do_death(0);
}
@ -288,7 +292,7 @@ main(int argc, char *argv[])
printf("Unable to modify filters\r\n");
do_death(0);
}
if (change_table(1, luser, ipsrc) == -1) {
if (change_table(1, ipsrc) == -1) {
printf("Unable to modify table\r\n");
change_filter(0, luser, ipsrc);
do_death(0);
@ -299,7 +303,7 @@ main(int argc, char *argv[])
signal(SIGALRM, need_death);
signal(SIGPIPE, need_death);
signal(SIGHUP, need_death);
signal(SIGSTOP, need_death);
signal(SIGQUIT, need_death);
signal(SIGTSTP, need_death);
while (1) {
printf("\r\nHello %s. ", luser);
@ -547,9 +551,11 @@ check_luser(char *luserdir, char *luser)
while (fputs(tmp, stdout) != EOF && !feof(f)) {
if (fgets(tmp, sizeof(tmp), f) == NULL) {
fflush(stdout);
fclose(f);
return (0);
}
}
fclose(f);
}
fflush(stdout);
return (0);
@ -633,6 +639,7 @@ change_filter(int add, const char *luser, const char *ipsrc)
char *fdpath = NULL, *userstr = NULL, *ipstr = NULL;
char *rsn = NULL, *fn = NULL;
pid_t pid;
gid_t gid;
int s;
if (luser == NULL || !luser[0] || ipsrc == NULL || !ipsrc[0]) {
@ -672,8 +679,14 @@ change_filter(int add, const char *luser, const char *ipsrc)
switch (pid = fork()) {
case -1:
err(1, "fork failed");
syslog(LOG_ERR, "fork failed");
goto error;
case 0:
/* revoke group privs before exec */
gid = getgid();
if (setregid(gid, gid) == -1) {
err(1, "setregid");
}
execvp(PATH_PFCTL, pargv);
warn("exec of %s failed", PATH_PFCTL);
_exit(1);
@ -682,10 +695,8 @@ change_filter(int add, const char *luser, const char *ipsrc)
/* parent */
waitpid(pid, &s, 0);
if (s != 0) {
if (WIFEXITED(s)) {
syslog(LOG_ERR, "pfctl exited abnormally");
goto error;
}
syslog(LOG_ERR, "pfctl exited abnormally");
goto error;
}
if (add) {
@ -701,16 +712,10 @@ change_filter(int add, const char *luser, const char *ipsrc)
syslog(LOG_ERR, "malloc failed");
error:
free(fdpath);
fdpath = NULL;
free(rsn);
rsn = NULL;
free(userstr);
userstr = NULL;
free(ipstr);
ipstr = NULL;
free(fn);
fn = NULL;
infile = NULL;
return (-1);
}
@ -718,13 +723,14 @@ change_filter(int add, const char *luser, const char *ipsrc)
* Add/remove this IP from the "authpf_users" table.
*/
static int
change_table(int add, const char *luser, const char *ipsrc)
change_table(int add, const char *ipsrc)
{
struct pfioc_table io;
struct pfr_addr addr;
bzero(&io, sizeof(io));
strlcpy(io.pfrio_table.pfrt_name, tablename, sizeof(io.pfrio_table));
strlcpy(io.pfrio_table.pfrt_name, tablename,
sizeof(io.pfrio_table.pfrt_name));
io.pfrio_buffer = &addr;
io.pfrio_esize = sizeof(addr);
io.pfrio_size = 1;
@ -813,13 +819,11 @@ do_death(int active)
if (active) {
change_filter(0, luser, ipsrc);
change_table(0, luser, ipsrc);
change_table(0, ipsrc);
authpf_kill_states();
remove_stale_rulesets();
}
if (pidfp)
ftruncate(fileno(pidfp), 0);
if (pidfile[0])
if (pidfile[0] && (pidfp != NULL))
if (unlink(pidfile) == -1)
syslog(LOG_ERR, "cannot unlink %s (%m)", pidfile);
exit(ret);

View File

@ -0,0 +1,387 @@
/* $OpenBSD: filter.c,v 1.5 2006/12/01 07:31:21 camield Exp $ */
/*
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "filter.h"
/* From netinet/in.h, but only _KERNEL_ gets them. */
#define satosin(sa) ((struct sockaddr_in *)(sa))
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
u_int16_t);
int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
struct sockaddr_in *);
int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
struct sockaddr_in6 *);
static struct pfioc_pooladdr pfp;
static struct pfioc_rule pfr;
static struct pfioc_trans pft;
static struct pfioc_trans_e pfte[TRANS_SIZE];
static int dev, rule_log;
static char *qname;
int
add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
struct sockaddr *dst, u_int16_t d_port)
{
if (!src || !dst || !d_port) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1)
return (-1);
pfr.rule.direction = dir;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
u_int16_t nat_range_high)
{
if (!src || !dst || !d_port || !nat || !nat_range_low ||
(src->sa_family != nat->sa_family)) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1)
return (-1);
if (nat->sa_family == AF_INET) {
memcpy(&pfp.addr.addr.v.a.addr.v4,
&satosin(nat)->sin_addr.s_addr, 4);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfp.addr.addr.v.a.addr.v6,
&satosin6(nat)->sin6_addr.s6_addr, 16);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
}
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
return (-1);
pfr.rule.rpool.proxy_port[0] = nat_range_low;
pfr.rule.rpool.proxy_port[1] = nat_range_high;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port)
{
if (!src || !dst || !d_port || !rdr || !rdr_port ||
(src->sa_family != rdr->sa_family)) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1)
return (-1);
if (rdr->sa_family == AF_INET) {
memcpy(&pfp.addr.addr.v.a.addr.v4,
&satosin(rdr)->sin_addr.s_addr, 4);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfp.addr.addr.v.a.addr.v6,
&satosin6(rdr)->sin6_addr.s6_addr, 16);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
}
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
return (-1);
pfr.rule.rpool.proxy_port[0] = rdr_port;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
do_commit(void)
{
if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
return (-1);
return (0);
}
int
do_rollback(void)
{
if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
return (-1);
return (0);
}
void
init_filter(char *opt_qname, int opt_verbose)
{
struct pf_status status;
qname = opt_qname;
if (opt_verbose == 1)
rule_log = PF_LOG;
else if (opt_verbose == 2)
rule_log = PF_LOG_ALL;
dev = open("/dev/pf", O_RDWR);
if (dev == -1)
err(1, "/dev/pf");
if (ioctl(dev, DIOCGETSTATUS, &status) == -1)
err(1, "DIOCGETSTATUS");
if (!status.running)
errx(1, "pf is disabled");
}
int
prepare_commit(u_int32_t id)
{
char an[PF_ANCHOR_NAME_SIZE];
int i;
memset(&pft, 0, sizeof pft);
pft.size = TRANS_SIZE;
pft.esize = sizeof pfte[0];
pft.array = pfte;
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
getpid(), id);
for (i = 0; i < TRANS_SIZE; i++) {
memset(&pfte[i], 0, sizeof pfte[0]);
strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
switch (i) {
case TRANS_FILTER:
pfte[i].rs_num = PF_RULESET_FILTER;
break;
case TRANS_NAT:
pfte[i].rs_num = PF_RULESET_NAT;
break;
case TRANS_RDR:
pfte[i].rs_num = PF_RULESET_RDR;
break;
default:
errno = EINVAL;
return (-1);
}
}
if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
return (-1);
return (0);
}
int
prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
struct sockaddr *dst, u_int16_t d_port)
{
char an[PF_ANCHOR_NAME_SIZE];
if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
(src->sa_family != dst->sa_family)) {
errno = EPROTONOSUPPORT;
return (-1);
}
memset(&pfp, 0, sizeof pfp);
memset(&pfr, 0, sizeof pfr);
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
getpid(), id);
strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
switch (rs_num) {
case PF_RULESET_FILTER:
pfr.ticket = pfte[TRANS_FILTER].ticket;
break;
case PF_RULESET_NAT:
pfr.ticket = pfte[TRANS_NAT].ticket;
break;
case PF_RULESET_RDR:
pfr.ticket = pfte[TRANS_RDR].ticket;
break;
default:
errno = EINVAL;
return (-1);
}
if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
return (-1);
pfr.pool_ticket = pfp.ticket;
/* Generic for all rule types. */
pfr.rule.af = src->sa_family;
pfr.rule.proto = IPPROTO_TCP;
pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
if (src->sa_family == AF_INET) {
memcpy(&pfr.rule.src.addr.v.a.addr.v4,
&satosin(src)->sin_addr.s_addr, 4);
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
&satosin(dst)->sin_addr.s_addr, 4);
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfr.rule.src.addr.v.a.addr.v6,
&satosin6(src)->sin6_addr.s6_addr, 16);
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
&satosin6(dst)->sin6_addr.s6_addr, 16);
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
}
pfr.rule.dst.port_op = PF_OP_EQ;
pfr.rule.dst.port[0] = htons(d_port);
switch (rs_num) {
case PF_RULESET_FILTER:
/*
* pass quick [log] inet[6] proto tcp \
* from $src to $dst port = $d_port flags S/SA keep state
* (max 1) [queue qname]
*/
pfr.rule.action = PF_PASS;
pfr.rule.quick = 1;
pfr.rule.log = rule_log;
pfr.rule.keep_state = 1;
pfr.rule.flags = TH_SYN;
pfr.rule.flagset = (TH_SYN|TH_ACK);
pfr.rule.max_states = 1;
if (qname != NULL)
strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
break;
case PF_RULESET_NAT:
/*
* nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
*/
pfr.rule.action = PF_NAT;
break;
case PF_RULESET_RDR:
/*
* rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
*/
pfr.rule.action = PF_RDR;
break;
default:
errno = EINVAL;
return (-1);
}
return (0);
}
int
server_lookup(struct sockaddr *client, struct sockaddr *proxy,
struct sockaddr *server)
{
if (client->sa_family == AF_INET)
return (server_lookup4(satosin(client), satosin(proxy),
satosin(server)));
if (client->sa_family == AF_INET6)
return (server_lookup6(satosin6(client), satosin6(proxy),
satosin6(server)));
errno = EPROTONOSUPPORT;
return (-1);
}
int
server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
struct sockaddr_in *server)
{
struct pfioc_natlook pnl;
memset(&pnl, 0, sizeof pnl);
pnl.direction = PF_OUT;
pnl.af = AF_INET;
pnl.proto = IPPROTO_TCP;
memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
pnl.sport = client->sin_port;
pnl.dport = proxy->sin_port;
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
return (-1);
memset(server, 0, sizeof(struct sockaddr_in));
server->sin_len = sizeof(struct sockaddr_in);
server->sin_family = AF_INET;
memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
sizeof server->sin_addr.s_addr);
server->sin_port = pnl.rdport;
return (0);
}
int
server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
struct sockaddr_in6 *server)
{
struct pfioc_natlook pnl;
memset(&pnl, 0, sizeof pnl);
pnl.direction = PF_OUT;
pnl.af = AF_INET6;
pnl.proto = IPPROTO_TCP;
memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
pnl.sport = client->sin6_port;
pnl.dport = proxy->sin6_port;
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
return (-1);
memset(server, 0, sizeof(struct sockaddr_in6));
server->sin6_len = sizeof(struct sockaddr_in6);
server->sin6_family = AF_INET6;
memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
sizeof server->sin6_addr);
server->sin6_port = pnl.rdport;
return (0);
}

View File

@ -0,0 +1,31 @@
/* $OpenBSD: filter.h,v 1.3 2005/06/07 14:12:07 camield Exp $ */
/*
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
*
* 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.
*/
#define FTP_PROXY_ANCHOR "ftp-proxy"
int add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *,
u_int16_t);
int add_nat(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
struct sockaddr *, u_int16_t, u_int16_t);
int add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
struct sockaddr *, u_int16_t);
int do_commit(void);
int do_rollback(void);
void init_filter(char *, int);
int prepare_commit(u_int32_t);
int server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *);

View File

@ -1,293 +1,183 @@
.\" $OpenBSD: ftp-proxy.8,v 1.42 2004/11/19 00:47:23 jmc Exp $
.\" $OpenBSD: ftp-proxy.8,v 1.7 2006/12/30 13:01:54 camield Exp $
.\"
.\" Copyright (c) 1996-2001
.\" Obtuse Systems Corporation, All rights reserved.
.\" Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must 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.
.\" 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.
.\"
.\" 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.
.\" 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 17, 2001
.Dd November 28, 2004
.Dt FTP-PROXY 8
.Os
.Sh NAME
.Nm ftp-proxy
.Nd Internet File Transfer Protocol proxy server
.Nd Internet File Transfer Protocol proxy daemon
.Sh SYNOPSIS
.Nm ftp-proxy
.Bk -words
.Op Fl AnrVw
.Op Fl 6Adrv
.Op Fl a Ar address
.Op Fl D Ar debuglevel
.Op Fl g Ar group
.Op Fl M Ar maxport
.Op Fl m Ar minport
.Op Fl R Ar address[:port]
.Op Fl S Ar address
.Op Fl b Ar address
.Op Fl D Ar level
.Op Fl m Ar maxsessions
.Op Fl P Ar port
.Op Fl p Ar port
.Op Fl q Ar queue
.Op Fl R Ar address
.Op Fl t Ar timeout
.Op Fl u Ar user
.Ek
.Sh DESCRIPTION
.Nm
is a proxy for the Internet File Transfer Protocol.
The proxy uses
FTP control connections should be redirected into the proxy using the
.Xr pf 4
and expects to have the FTP control connection as described in
.Xr services 5
redirected to it via a
.Ar rdr
command, after which the proxy connects to the server on behalf of
the client.
.Pp
The proxy allows data connections to pass, rewriting and redirecting
them so that the right addresses are used.
All connections from the client to the server have their source
address rewritten so they appear to come from the proxy.
Consequently, all connections from the server to the proxy have
their destination address rewritten, so they are redirected to the
client.
The proxy uses the
.Xr pf 4
.Em rdr
command.
An example of how to do that is further down in this document.
.Ar anchor
facility for this.
.Pp
Assuming the FTP control connection is from $client to $server, the
proxy connected to the server using the $proxy source address, and
$port is negotiated, then
.Nm ftp-proxy
adds the following rules to the various anchors.
(These example rules use inet, but the proxy also supports inet6.)
.Pp
In case of active mode (PORT or EPRT):
.Bd -literal -offset 2n
rdr from $server to $proxy port $port -> $client
pass quick inet proto tcp \e
from $server to $client port $port
.Ed
.Pp
In case of passive mode (PASV or EPSV):
.Bd -literal -offset 2n
nat from $client to $server port $port -> $proxy
pass in quick inet proto tcp \e
from $client to $server port $port
pass out quick inet proto tcp \e
from $proxy to $server port $port
.Ed
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl 6
IPv6 mode.
The proxy will expect and use IPv6 addresses for all communication.
Only the extended FTP modes EPSV and EPRT are allowed with IPv6.
The proxy is in IPv4 mode by default.
.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.
Only permit anonymous FTP connections.
Either user "ftp" or user "anonymous" is allowed.
.It Fl a Ar address
Specify the local IP address to use in
.Xr bind 2
as the source for connections made by
.Nm ftp-proxy
when connecting to destination FTP servers.
This may be necessary if the interface address of
your default route is not reachable from the destinations
.Nm
is attempting connections to, or this address is different from the one
connections are being NATed to.
In the usual case this means that
.Ar address
should be a publicly visible IP address assigned to one of
the interfaces on the machine running
.Nm
and should be the same address to which you are translating traffic
if you are using the
.Fl n
option.
.It Fl D Ar debuglevel
Specify a debug level, where the proxy emits verbose debug output
into
.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 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 m Ar minport
Specify the lower end of the port range the proxy will use for all
data connections it establishes.
The default is
.Dv IPPORT_HIFIRSTAUTO
defined in
.Aq Pa netinet/in.h
as 49152.
.It Fl n
Activate network address translation
.Pq NAT
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 Ar address:[port]
Reverse proxy mode for FTP servers running behind a NAT gateway.
In this mode, no redirection is needed.
The proxy is run from
.Xr inetd 8
on the port that external clients connect to (usually 21).
Control connections and passive data connections are forwarded
to the server.
The proxy will use this as the source address for the control
connection to a server.
.It Fl b Ar address
Address where the proxy will listen for redirected control connections.
The default is 127.0.0.1, or ::1 in IPv6 mode.
.It Fl D Ar level
Debug level, ranging from 0 to 7.
Higher is more verbose.
The default is 5.
(These levels correspond to the
.Xr syslog 3
levels.)
.It Fl d
Do not daemonize.
The process will stay in the foreground, logging to standard error.
.It Fl m Ar maxsessions
Maximum number of concurrent FTP sessions.
When the proxy reaches this limit, new connections are denied.
The default is 100 sessions.
The limit can be lowered to a minimum of 1, or raised to a maximum of 500.
.It Fl P Ar port
Fixed server port.
Only used in combination with
.Fl R .
The default is port 21.
.It Fl p Ar port
Port where the proxy will listen for redirected connections.
The default is port 8021.
.It Fl q Ar queue
Create rules with queue
.Ar queue
appended, so that data connections can be queued.
.It Fl R Ar address
Fixed server address, also known as reverse mode.
The proxy will always connect to the same server, regardless of
where the client wanted to connect to (before it was redirected).
Use this option to proxy for a server behind NAT, or to forward all
connections to another proxy.
.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 S Ar address
Source address to use for data connections made by the proxy.
Useful when there are multiple addresses (aliases) available
to the proxy.
Clients may expect data connections to have the same source
address as the control connections, and reject or drop other
connections.
Rewrite sourceport to 20 in active mode to suit ancient clients that insist
on this RFC property.
.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.
Number of seconds that the control connection can be idle, before the
proxy will disconnect.
The maximum is 86400 seconds, which is also the default.
Do not set this too low, because the control connection is usually
idle when large data transfers are taking place.
.It Fl v
Set the 'log' flag on pf rules committed by
.Nm .
Use twice to set the 'log-all' flag.
The pf rules do not log by default.
.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
.Sh CONFIGURATION
To make use of the proxy,
.Xr pf.conf 5
rule such as
needs the following rules.
All anchors are mandatory.
Adjust the rules as needed.
.Pp
In the NAT section:
.Bd -literal -offset 2n
int_if = \&"xl0\&"
rdr pass on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr pass on $int_if proto tcp from $lan to any port 21 -> \e
127.0.0.1 port 8021
.Ed
.Pp
.Xr inetd 8
must then be configured to run
.Nm
on the port from above using
In the rule section:
.Bd -literal -offset 2n
127.0.0.1:8021 stream tcp nowait root /usr/libexec/ftp-proxy ftp-proxy
anchor "ftp-proxy/*"
pass out proto tcp from $proxy to any port 21
.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.
.Sh CAVEATS
.Xr pf 4
does not allow the ruleset to be modified if the system is running at a
.Xr securelevel 7
higher than 1.
At that level
.Nm ftp-proxy
cannot add rules to the anchors and FTP data connections may get blocked.
.Pp
IPv6 is not yet supported.
Negotiated data connection ports below 1024 are not allowed.
.Pp
The negotiated IP address for active modes is ignored for security
reasons.
This makes third party file transfers impossible.
.Pp
.Nm ftp-proxy
chroots to "/var/empty" and changes to user "proxy" to drop privileges.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,456 @@
/*
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_VASPRINTF
/* If we have vasprintf, we need to define this before we include stdio.h. */
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "event.h"
struct evbuffer *
evbuffer_new(void)
{
struct evbuffer *buffer;
buffer = calloc(1, sizeof(struct evbuffer));
return (buffer);
}
void
evbuffer_free(struct evbuffer *buffer)
{
if (buffer->orig_buffer != NULL)
free(buffer->orig_buffer);
free(buffer);
}
/*
* This is a destructive add. The data from one buffer moves into
* the other buffer.
*/
#define SWAP(x,y) do { \
(x)->buffer = (y)->buffer; \
(x)->orig_buffer = (y)->orig_buffer; \
(x)->misalign = (y)->misalign; \
(x)->totallen = (y)->totallen; \
(x)->off = (y)->off; \
} while (0)
int
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
{
int res;
/* Short cut for better performance */
if (outbuf->off == 0) {
struct evbuffer tmp;
size_t oldoff = inbuf->off;
/* Swap them directly */
SWAP(&tmp, outbuf);
SWAP(outbuf, inbuf);
SWAP(inbuf, &tmp);
/*
* Optimization comes with a price; we need to notify the
* buffer if necessary of the changes. oldoff is the amount
* of data that we tranfered from inbuf to outbuf
*/
if (inbuf->off != oldoff && inbuf->cb != NULL)
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
if (oldoff && outbuf->cb != NULL)
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
return (0);
}
res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
if (res == 0) {
/* We drain the input buffer on success */
evbuffer_drain(inbuf, inbuf->off);
}
return (res);
}
int
evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
{
char *buffer;
size_t space;
size_t oldoff = buf->off;
int sz;
va_list aq;
for (;;) {
buffer = (char *)buf->buffer + buf->off;
space = buf->totallen - buf->misalign - buf->off;
#ifndef va_copy
#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
#endif
va_copy(aq, ap);
#ifdef WIN32
sz = vsnprintf(buffer, space - 1, fmt, aq);
buffer[space - 1] = '\0';
#else
sz = vsnprintf(buffer, space, fmt, aq);
#endif
va_end(aq);
if (sz == -1)
return (-1);
if (sz < space) {
buf->off += sz;
if (buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
return (sz);
}
if (evbuffer_expand(buf, sz + 1) == -1)
return (-1);
}
/* NOTREACHED */
}
int
evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
{
int res = -1;
va_list ap;
va_start(ap, fmt);
res = evbuffer_add_vprintf(buf, fmt, ap);
va_end(ap);
return (res);
}
/* Reads data from an event buffer and drains the bytes read */
int
evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
{
size_t nread = datlen;
if (nread >= buf->off)
nread = buf->off;
memcpy(data, buf->buffer, nread);
evbuffer_drain(buf, nread);
return (nread);
}
/*
* Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
* The returned buffer needs to be freed by the called.
*/
char *
evbuffer_readline(struct evbuffer *buffer)
{
u_char *data = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
char *line;
unsigned int i;
for (i = 0; i < len; i++) {
if (data[i] == '\r' || data[i] == '\n')
break;
}
if (i == len)
return (NULL);
if ((line = malloc(i + 1)) == NULL) {
fprintf(stderr, "%s: out of memory\n", __func__);
evbuffer_drain(buffer, i);
return (NULL);
}
memcpy(line, data, i);
line[i] = '\0';
/*
* Some protocols terminate a line with '\r\n', so check for
* that, too.
*/
if ( i < len - 1 ) {
char fch = data[i], sch = data[i+1];
/* Drain one more character if needed */
if ( (sch == '\r' || sch == '\n') && sch != fch )
i += 1;
}
evbuffer_drain(buffer, i + 1);
return (line);
}
/* Adds data to an event buffer */
static inline void
evbuffer_align(struct evbuffer *buf)
{
memmove(buf->orig_buffer, buf->buffer, buf->off);
buf->buffer = buf->orig_buffer;
buf->misalign = 0;
}
/* Expands the available space in the event buffer to at least datlen */
int
evbuffer_expand(struct evbuffer *buf, size_t datlen)
{
size_t need = buf->misalign + buf->off + datlen;
/* If we can fit all the data, then we don't have to do anything */
if (buf->totallen >= need)
return (0);
/*
* If the misalignment fulfills our data needs, we just force an
* alignment to happen. Afterwards, we have enough space.
*/
if (buf->misalign >= datlen) {
evbuffer_align(buf);
} else {
void *newbuf;
size_t length = buf->totallen;
if (length < 256)
length = 256;
while (length < need)
length <<= 1;
if (buf->orig_buffer != buf->buffer)
evbuffer_align(buf);
if ((newbuf = realloc(buf->buffer, length)) == NULL)
return (-1);
buf->orig_buffer = buf->buffer = newbuf;
buf->totallen = length;
}
return (0);
}
int
evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen)
{
size_t need = buf->misalign + buf->off + datlen;
size_t oldoff = buf->off;
if (buf->totallen < need) {
if (evbuffer_expand(buf, datlen) == -1)
return (-1);
}
memcpy(buf->buffer + buf->off, data, datlen);
buf->off += datlen;
if (datlen && buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
return (0);
}
void
evbuffer_drain(struct evbuffer *buf, size_t len)
{
size_t oldoff = buf->off;
if (len >= buf->off) {
buf->off = 0;
buf->buffer = buf->orig_buffer;
buf->misalign = 0;
goto done;
}
buf->buffer += len;
buf->misalign += len;
buf->off -= len;
done:
/* Tell someone about changes in this buffer */
if (buf->off != oldoff && buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
}
/*
* Reads data from a file descriptor into a buffer.
*/
#define EVBUFFER_MAX_READ 4096
int
evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
{
u_char *p;
size_t oldoff = buf->off;
int n = EVBUFFER_MAX_READ;
#ifdef WIN32
DWORD dwBytesRead;
#endif
#ifdef FIONREAD
if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
n = EVBUFFER_MAX_READ;
} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
/*
* It's possible that a lot of data is available for
* reading. We do not want to exhaust resources
* before the reader has a chance to do something
* about it. If the reader does not tell us how much
* data we should read, we artifically limit it.
*/
if (n > buf->totallen << 2)
n = buf->totallen << 2;
if (n < EVBUFFER_MAX_READ)
n = EVBUFFER_MAX_READ;
}
#endif
if (howmuch < 0 || howmuch > n)
howmuch = n;
/* If we don't have FIONREAD, we might waste some space here */
if (evbuffer_expand(buf, howmuch) == -1)
return (-1);
/* We can append new data at this point */
p = buf->buffer + buf->off;
#ifndef WIN32
n = read(fd, p, howmuch);
if (n == -1)
return (-1);
if (n == 0)
return (0);
#else
n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL);
if (n == 0)
return (-1);
if (dwBytesRead == 0)
return (0);
n = dwBytesRead;
#endif
buf->off += n;
/* Tell someone about changes in this buffer */
if (buf->off != oldoff && buf->cb != NULL)
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
return (n);
}
int
evbuffer_write(struct evbuffer *buffer, int fd)
{
int n;
#ifdef WIN32
DWORD dwBytesWritten;
#endif
#ifndef WIN32
n = write(fd, buffer->buffer, buffer->off);
if (n == -1)
return (-1);
if (n == 0)
return (0);
#else
n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL);
if (n == 0)
return (-1);
if (dwBytesWritten == 0)
return (0);
n = dwBytesWritten;
#endif
evbuffer_drain(buffer, n);
return (n);
}
u_char *
evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
{
size_t remain = buffer->off;
u_char *search = buffer->buffer;
u_char *p;
while ((p = memchr(search, *what, remain)) != NULL) {
remain = buffer->off - (size_t)(search - buffer->buffer);
if (remain < len)
break;
if (memcmp(p, what, len) == 0)
return (p);
search = p + 1;
}
return (NULL);
}
void evbuffer_setcb(struct evbuffer *buffer,
void (*cb)(struct evbuffer *, size_t, size_t, void *),
void *cbarg)
{
buffer->cb = cb;
buffer->cbarg = cbarg;
}

View File

@ -0,0 +1,413 @@
/*
* Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include "event.h"
/* prototypes */
void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
static int
bufferevent_add(struct event *ev, int timeout)
{
struct timeval tv, *ptv = NULL;
if (timeout) {
timerclear(&tv);
tv.tv_sec = timeout;
ptv = &tv;
}
return (event_add(ev, ptv));
}
/*
* This callback is executed when the size of the input buffer changes.
* We use it to apply back pressure on the reading side.
*/
void
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
void *arg) {
struct bufferevent *bufev = arg;
/*
* If we are below the watermark then reschedule reading if it's
* still enabled.
*/
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
evbuffer_setcb(buf, NULL, NULL);
if (bufev->enabled & EV_READ)
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
}
}
static void
bufferevent_readcb(int fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
int res = 0;
short what = EVBUFFER_READ;
size_t len;
int howmuch = -1;
if (event == EV_TIMEOUT) {
what |= EVBUFFER_TIMEOUT;
goto error;
}
/*
* If we have a high watermark configured then we don't want to
* read more data than would make us reach the watermark.
*/
if (bufev->wm_read.high != 0)
howmuch = bufev->wm_read.high;
res = evbuffer_read(bufev->input, fd, howmuch);
if (res == -1) {
if (errno == EAGAIN || errno == EINTR)
goto reschedule;
/* error case */
what |= EVBUFFER_ERROR;
} else if (res == 0) {
/* eof case */
what |= EVBUFFER_EOF;
}
if (res <= 0)
goto error;
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
/* See if this callbacks meets the water marks */
len = EVBUFFER_LENGTH(bufev->input);
if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
return;
if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
struct evbuffer *buf = bufev->input;
event_del(&bufev->ev_read);
/* Now schedule a callback for us */
evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
return;
}
/* Invoke the user callback - must always be called last */
if (bufev->readcb != NULL)
(*bufev->readcb)(bufev, bufev->cbarg);
return;
reschedule:
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
return;
error:
(*bufev->errorcb)(bufev, what, bufev->cbarg);
}
static void
bufferevent_writecb(int fd, short event, void *arg)
{
struct bufferevent *bufev = arg;
int res = 0;
short what = EVBUFFER_WRITE;
if (event == EV_TIMEOUT) {
what |= EVBUFFER_TIMEOUT;
goto error;
}
if (EVBUFFER_LENGTH(bufev->output)) {
res = evbuffer_write(bufev->output, fd);
if (res == -1) {
#ifndef WIN32
/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
*set errno. thus this error checking is not portable*/
if (errno == EAGAIN ||
errno == EINTR ||
errno == EINPROGRESS)
goto reschedule;
/* error case */
what |= EVBUFFER_ERROR;
#else
goto reschedule;
#endif
} else if (res == 0) {
/* eof case */
what |= EVBUFFER_EOF;
}
if (res <= 0)
goto error;
}
if (EVBUFFER_LENGTH(bufev->output) != 0)
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
/*
* Invoke the user callback if our buffer is drained or below the
* low watermark.
*/
if (bufev->writecb != NULL &&
EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
(*bufev->writecb)(bufev, bufev->cbarg);
return;
reschedule:
if (EVBUFFER_LENGTH(bufev->output) != 0)
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
return;
error:
(*bufev->errorcb)(bufev, what, bufev->cbarg);
}
/*
* Create a new buffered event object.
*
* The read callback is invoked whenever we read new data.
* The write callback is invoked whenever the output buffer is drained.
* The error callback is invoked on a write/read error or on EOF.
*
* Both read and write callbacks maybe NULL. The error callback is not
* allowed to be NULL and have to be provided always.
*/
struct bufferevent *
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
everrorcb errorcb, void *cbarg)
{
struct bufferevent *bufev;
if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
return (NULL);
if ((bufev->input = evbuffer_new()) == NULL) {
free(bufev);
return (NULL);
}
if ((bufev->output = evbuffer_new()) == NULL) {
evbuffer_free(bufev->input);
free(bufev);
return (NULL);
}
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
bufev->readcb = readcb;
bufev->writecb = writecb;
bufev->errorcb = errorcb;
bufev->cbarg = cbarg;
/*
* Set to EV_WRITE so that using bufferevent_write is going to
* trigger a callback. Reading needs to be explicitly enabled
* because otherwise no data will be available.
*/
bufev->enabled = EV_WRITE;
return (bufev);
}
int
bufferevent_priority_set(struct bufferevent *bufev, int priority)
{
if (event_priority_set(&bufev->ev_read, priority) == -1)
return (-1);
if (event_priority_set(&bufev->ev_write, priority) == -1)
return (-1);
return (0);
}
/* Closing the file descriptor is the responsibility of the caller */
void
bufferevent_free(struct bufferevent *bufev)
{
event_del(&bufev->ev_read);
event_del(&bufev->ev_write);
evbuffer_free(bufev->input);
evbuffer_free(bufev->output);
free(bufev);
}
/*
* Returns 0 on success;
* -1 on failure.
*/
int
bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
{
int res;
res = evbuffer_add(bufev->output, data, size);
if (res == -1)
return (res);
/* If everything is okay, we need to schedule a write */
if (size > 0 && (bufev->enabled & EV_WRITE))
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
return (res);
}
int
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
{
int res;
res = bufferevent_write(bufev, buf->buffer, buf->off);
if (res != -1)
evbuffer_drain(buf, buf->off);
return (res);
}
size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
struct evbuffer *buf = bufev->input;
if (buf->off < size)
size = buf->off;
/* Copy the available data to the user buffer */
memcpy(data, buf->buffer, size);
if (size)
evbuffer_drain(buf, size);
return (size);
}
int
bufferevent_enable(struct bufferevent *bufev, short event)
{
if (event & EV_READ) {
if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
return (-1);
}
if (event & EV_WRITE) {
if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
return (-1);
}
bufev->enabled |= event;
return (0);
}
int
bufferevent_disable(struct bufferevent *bufev, short event)
{
if (event & EV_READ) {
if (event_del(&bufev->ev_read) == -1)
return (-1);
}
if (event & EV_WRITE) {
if (event_del(&bufev->ev_write) == -1)
return (-1);
}
bufev->enabled &= ~event;
return (0);
}
/*
* Sets the read and write timeout for a buffered event.
*/
void
bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write) {
bufev->timeout_read = timeout_read;
bufev->timeout_write = timeout_write;
}
/*
* Sets the water marks
*/
void
bufferevent_setwatermark(struct bufferevent *bufev, short events,
size_t lowmark, size_t highmark)
{
if (events & EV_READ) {
bufev->wm_read.low = lowmark;
bufev->wm_read.high = highmark;
}
if (events & EV_WRITE) {
bufev->wm_write.low = lowmark;
bufev->wm_write.high = highmark;
}
/* If the watermarks changed then see if we should call read again */
bufferevent_read_pressure_cb(bufev->input,
0, EVBUFFER_LENGTH(bufev->input), bufev);
}
int
bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
{
int res;
res = event_base_set(base, &bufev->ev_read);
if (res == -1)
return (res);
res = event_base_set(base, &bufev->ev_write);
return (res);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifndef _EVENT_INTERNAL_H_
#define _EVENT_INTERNAL_H_
#ifdef __cplusplus
extern "C" {
#endif
struct event_base {
const struct eventop *evsel;
void *evbase;
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
/* active event management */
struct event_list **activequeues;
int nactivequeues;
struct event_list eventqueue;
struct timeval event_tv;
RB_HEAD(event_tree, event) timetree;
};
#ifdef __cplusplus
}
#endif
#endif /* _EVENT_INTERNAL_H_ */

878
contrib/pf/libevent/event.c Normal file
View File

@ -0,0 +1,878 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include "misc.h"
#endif
#include <sys/types.h>
#include <sys/tree.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#include "event.h"
#include "event-internal.h"
#include "log.h"
#ifdef HAVE_EVENT_PORTS
extern const struct eventop evportops;
#endif
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern const struct eventop pollops;
#endif
#ifdef HAVE_RTSIG
extern const struct eventop rtsigops;
#endif
#ifdef HAVE_EPOLL
extern const struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
#ifdef HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif
/* In order of preference */
const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_DEVPOLL
&devpollops,
#endif
#ifdef HAVE_RTSIG
&rtsigops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
/* Global state */
struct event_list signalqueue;
struct event_base *current_base = NULL;
/* Handle signals - This is a deprecated interface */
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
volatile sig_atomic_t event_gotsig; /* Set in signal handler */
/* Prototypes */
static void event_queue_insert(struct event_base *, struct event *, int);
static void event_queue_remove(struct event_base *, struct event *, int);
static int event_haveevents(struct event_base *);
static void event_process_active(struct event_base *);
static int timeout_next(struct event_base *, struct timeval *);
static void timeout_process(struct event_base *);
static void timeout_correct(struct event_base *, struct timeval *);
static int
compare(struct event *a, struct event *b)
{
if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
return (-1);
else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
return (1);
if (a < b)
return (-1);
else if (a > b)
return (1);
return (0);
}
static int
gettime(struct timeval *tp)
{
#ifdef HAVE_CLOCK_GETTIME
struct timespec ts;
#ifdef HAVE_CLOCK_MONOTONIC
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
#else
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
#endif
return (-1);
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
#else
gettimeofday(tp, NULL);
#endif
return (0);
}
RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
RB_GENERATE(event_tree, event, ev_timeout_node, compare);
void *
event_init(void)
{
int i;
if ((current_base = calloc(1, sizeof(struct event_base))) == NULL)
event_err(1, "%s: calloc");
event_sigcb = NULL;
event_gotsig = 0;
gettime(&current_base->event_tv);
RB_INIT(&current_base->timetree);
TAILQ_INIT(&current_base->eventqueue);
TAILQ_INIT(&signalqueue);
current_base->evbase = NULL;
for (i = 0; eventops[i] && !current_base->evbase; i++) {
current_base->evsel = eventops[i];
current_base->evbase = current_base->evsel->init();
}
if (current_base->evbase == NULL)
event_errx(1, "%s: no event mechanism available", __func__);
if (getenv("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s\n",
current_base->evsel->name);
/* allocate a single active event queue */
event_base_priority_init(current_base, 1);
return (current_base);
}
void
event_base_free(struct event_base *base)
{
int i;
if (base == NULL && current_base)
base = current_base;
if (base == current_base)
current_base = NULL;
assert(base);
assert(TAILQ_EMPTY(&base->eventqueue));
for (i=0; i < base->nactivequeues; ++i)
assert(TAILQ_EMPTY(base->activequeues[i]));
assert(RB_EMPTY(&base->timetree));
for (i = 0; i < base->nactivequeues; ++i)
free(base->activequeues[i]);
free(base->activequeues);
if (base->evsel->dealloc != NULL)
base->evsel->dealloc(base->evbase);
free(base);
}
int
event_priority_init(int npriorities)
{
return event_base_priority_init(current_base, npriorities);
}
int
event_base_priority_init(struct event_base *base, int npriorities)
{
int i;
if (base->event_count_active)
return (-1);
if (base->nactivequeues && npriorities != base->nactivequeues) {
for (i = 0; i < base->nactivequeues; ++i) {
free(base->activequeues[i]);
}
free(base->activequeues);
}
/* Allocate our priority queues */
base->nactivequeues = npriorities;
base->activequeues = (struct event_list **)calloc(base->nactivequeues,
npriorities * sizeof(struct event_list *));
if (base->activequeues == NULL)
event_err(1, "%s: calloc", __func__);
for (i = 0; i < base->nactivequeues; ++i) {
base->activequeues[i] = malloc(sizeof(struct event_list));
if (base->activequeues[i] == NULL)
event_err(1, "%s: malloc", __func__);
TAILQ_INIT(base->activequeues[i]);
}
return (0);
}
int
event_haveevents(struct event_base *base)
{
return (base->event_count > 0);
}
/*
* Active events are stored in priority queues. Lower priorities are always
* process before higher priorities. Low priority events can starve high
* priority ones.
*/
static void
event_process_active(struct event_base *base)
{
struct event *ev;
struct event_list *activeq = NULL;
int i;
short ncalls;
if (!base->event_count_active)
return;
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
activeq = base->activequeues[i];
break;
}
}
assert(activeq != NULL);
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
event_queue_remove(base, ev, EVLIST_ACTIVE);
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
if (event_gotsig)
return;
}
}
}
/*
* Wait continously for events. We exit only if no events are left.
*/
int
event_dispatch(void)
{
return (event_loop(0));
}
int
event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
static void
event_loopexit_cb(int fd, short what, void *arg)
{
struct event_base *base = arg;
base->event_gotterm = 1;
}
/* not thread safe */
int
event_loopexit(struct timeval *tv)
{
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
current_base, tv));
}
int
event_base_loopexit(struct event_base *event_base, struct timeval *tv)
{
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
event_base, tv));
}
/* not thread safe */
int
event_loop(int flags)
{
return event_base_loop(current_base, flags);
}
int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
int res, done;
done = 0;
while (!done) {
/* Calculate the initial events that we are waiting for */
if (evsel->recalc(base, evbase, 0) == -1)
return (-1);
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
base->event_gotterm = 0;
break;
}
/* You cannot use this interface for multi-threaded apps */
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
res = (*event_sigcb)();
if (res == -1) {
errno = EINTR;
return (-1);
}
}
}
/* Check if time is running backwards */
gettime(&tv);
if (timercmp(&tv, &base->event_tv, <)) {
struct timeval off;
event_debug(("%s: time is running backwards, corrected",
__func__));
timersub(&base->event_tv, &tv, &off);
timeout_correct(base, &off);
}
base->event_tv = tv;
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK))
timeout_next(base, &tv);
else
timerclear(&tv);
/* If we have no events, we just exit */
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
return (1);
}
res = evsel->dispatch(base, evbase, &tv);
if (res == -1)
return (-1);
timeout_process(base);
if (base->event_count_active) {
event_process_active(base);
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
/* Sets up an event for processing once */
struct event_once {
struct event ev;
void (*cb)(int, short, void *);
void *arg;
};
/* One-time callback, it deletes itself */
static void
event_once_cb(int fd, short events, void *arg)
{
struct event_once *eonce = arg;
(*eonce->cb)(fd, events, eonce->arg);
free(eonce);
}
/* Schedules an event once */
int
event_once(int fd, short events,
void (*callback)(int, short, void *), void *arg, struct timeval *tv)
{
struct event_once *eonce;
struct timeval etv;
int res;
/* We cannot support signals that just fire once */
if (events & EV_SIGNAL)
return (-1);
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
return (-1);
eonce->cb = callback;
eonce->arg = arg;
if (events == EV_TIMEOUT) {
if (tv == NULL) {
timerclear(&etv);
tv = &etv;
}
evtimer_set(&eonce->ev, event_once_cb, eonce);
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
} else {
/* Bad event combination */
free(eonce);
return (-1);
}
res = event_add(&eonce->ev, tv);
if (res != 0) {
free(eonce);
return (res);
}
return (0);
}
void
event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
/* Take the current base - caller needs to set the real base later */
ev->ev_base = current_base;
ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
/* by default, we put new events into the middle priority */
ev->ev_pri = current_base->nactivequeues/2;
}
int
event_base_set(struct event_base *base, struct event *ev)
{
/* Only innocent events may be assigned to a different base */
if (ev->ev_flags != EVLIST_INIT)
return (-1);
ev->ev_base = base;
ev->ev_pri = base->nactivequeues/2;
return (0);
}
/*
* Set's the priority of an event - if an event is already scheduled
* changing the priority is going to fail.
*/
int
event_priority_set(struct event *ev, int pri)
{
if (ev->ev_flags & EVLIST_ACTIVE)
return (-1);
if (pri < 0 || pri >= ev->ev_base->nactivequeues)
return (-1);
ev->ev_pri = pri;
return (0);
}
/*
* Checks if a specific event is pending or scheduled.
*/
int
event_pending(struct event *ev, short event, struct timeval *tv)
{
struct timeval now, res;
int flags = 0;
if (ev->ev_flags & EVLIST_INSERTED)
flags |= (ev->ev_events & (EV_READ|EV_WRITE));
if (ev->ev_flags & EVLIST_ACTIVE)
flags |= ev->ev_res;
if (ev->ev_flags & EVLIST_TIMEOUT)
flags |= EV_TIMEOUT;
if (ev->ev_flags & EVLIST_SIGNAL)
flags |= EV_SIGNAL;
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
/* See if there is a timeout that we should report */
if (tv != NULL && (flags & event & EV_TIMEOUT)) {
gettime(&now);
timersub(&ev->ev_timeout, &now, &res);
/* correctly remap to real time */
gettimeofday(&now, NULL);
timeradd(&now, &res, tv);
}
return (flags & event);
}
int
event_add(struct event *ev, struct timeval *tv)
{
struct event_base *base = ev->ev_base;
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
event_debug((
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
if (tv != NULL) {
struct timeval now;
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
gettime(&now);
timeradd(&now, tv, &ev->ev_timeout);
event_debug((
"event_add: timeout in %d seconds, call %p",
tv->tv_sec, ev->ev_callback));
event_queue_insert(base, ev, EVLIST_TIMEOUT);
}
if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
event_queue_insert(base, ev, EVLIST_INSERTED);
return (evsel->add(evbase, ev));
} else if ((ev->ev_events & EV_SIGNAL) &&
!(ev->ev_flags & EVLIST_SIGNAL)) {
event_queue_insert(base, ev, EVLIST_SIGNAL);
return (evsel->add(evbase, ev));
}
return (0);
}
int
event_del(struct event *ev)
{
struct event_base *base;
const struct eventop *evsel;
void *evbase;
event_debug(("event_del: %p, callback %p",
ev, ev->ev_callback));
/* An event without a base has not been added */
if (ev->ev_base == NULL)
return (-1);
base = ev->ev_base;
evsel = base->evsel;
evbase = base->evbase;
assert(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove(base, ev, EVLIST_ACTIVE);
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove(base, ev, EVLIST_INSERTED);
return (evsel->del(evbase, ev));
} else if (ev->ev_flags & EVLIST_SIGNAL) {
event_queue_remove(base, ev, EVLIST_SIGNAL);
return (evsel->del(evbase, ev));
}
return (0);
}
void
event_active(struct event *ev, int res, short ncalls)
{
/* We get different kinds of events, add them together */
if (ev->ev_flags & EVLIST_ACTIVE) {
ev->ev_res |= res;
return;
}
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}
int
timeout_next(struct event_base *base, struct timeval *tv)
{
struct timeval dflt = TIMEOUT_DEFAULT;
struct timeval now;
struct event *ev;
if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
*tv = dflt;
return (0);
}
if (gettime(&now) == -1)
return (-1);
if (timercmp(&ev->ev_timeout, &now, <=)) {
timerclear(tv);
return (0);
}
timersub(&ev->ev_timeout, &now, tv);
assert(tv->tv_sec >= 0);
assert(tv->tv_usec >= 0);
event_debug(("timeout_next: in %d seconds", tv->tv_sec));
return (0);
}
static void
timeout_correct(struct event_base *base, struct timeval *off)
{
struct event *ev;
/*
* We can modify the key element of the node without destroying
* the key, beause we apply it to all in the right order.
*/
RB_FOREACH(ev, event_tree, &base->timetree)
timersub(&ev->ev_timeout, off, &ev->ev_timeout);
}
void
timeout_process(struct event_base *base)
{
struct timeval now;
struct event *ev, *next;
gettime(&now);
for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
if (timercmp(&ev->ev_timeout, &now, >))
break;
next = RB_NEXT(event_tree, &base->timetree, ev);
event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* delete this event from the I/O queues */
event_del(ev);
event_debug(("timeout_process: call %p",
ev->ev_callback));
event_active(ev, EV_TIMEOUT, 1);
}
}
void
event_queue_remove(struct event_base *base, struct event *ev, int queue)
{
int docount = 1;
if (!(ev->ev_flags & queue))
event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
ev, ev->ev_fd, queue);
if (ev->ev_flags & EVLIST_INTERNAL)
docount = 0;
if (docount)
base->event_count--;
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_ACTIVE:
if (docount)
base->event_count_active--;
TAILQ_REMOVE(base->activequeues[ev->ev_pri],
ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT:
RB_REMOVE(event_tree, &base->timetree, ev);
break;
case EVLIST_INSERTED:
TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
break;
default:
event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
int docount = 1;
if (ev->ev_flags & queue) {
/* Double insertion is possible for active events */
if (queue & EVLIST_ACTIVE)
return;
event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
ev, ev->ev_fd, queue);
}
if (ev->ev_flags & EVLIST_INTERNAL)
docount = 0;
if (docount)
base->event_count++;
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_ACTIVE:
if (docount)
base->event_count_active++;
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
ev,ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT: {
struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
assert(tmp == NULL);
break;
}
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
break;
default:
event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
/* Functions for debugging */
const char *
event_get_version(void)
{
return (VERSION);
}
/*
* No thread-safe interface needed - the information should be the same
* for all threads.
*/
const char *
event_get_method(void)
{
return (current_base->evsel->name);
}

341
contrib/pf/libevent/event.h Normal file
View File

@ -0,0 +1,341 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifndef _EVENT_H_
#define _EVENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
typedef unsigned char u_char;
typedef unsigned short u_short;
#endif
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_SIGNAL 0x04
#define EVLIST_ACTIVE 0x08
#define EVLIST_INTERNAL 0x10
#define EVLIST_INIT 0x80
/* EVLIST_X_ Private space: 0x1000-0xf000 */
#define EVLIST_ALL (0xf000 | 0x9f)
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 /* Persistant event */
/* Fix so that ppl dont have to run with <sys/queue.h> */
#ifndef TAILQ_ENTRY
#define _EVENT_DEFINED_TQENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#endif /* !TAILQ_ENTRY */
#ifndef RB_ENTRY
#define _EVENT_DEFINED_RBENTRY
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#endif /* !RB_ENTRY */
struct event_base;
struct event {
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
RB_ENTRY (event) ev_timeout_node;
struct event_base *ev_base;
int ev_fd;
short ev_events;
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout;
int ev_pri; /* smaller numbers are higher priority */
void (*ev_callback)(int, short, void *arg);
void *ev_arg;
int ev_res; /* result passed to event callback */
int ev_flags;
};
#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd
#define EVENT_FD(ev) (int)(ev)->ev_fd
/*
* Key-Value pairs. Can be used for HTTP headers but also for
* query argument parsing.
*/
struct evkeyval {
TAILQ_ENTRY(evkeyval) next;
char *key;
char *value;
};
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
struct event_list;
struct evkeyvalq;
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
TAILQ_HEAD (evkeyvalq, evkeyval);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
#undef _EVENT_DEFINED_RBENTRY
#endif /* _EVENT_DEFINED_RBENTRY */
struct eventop {
char *name;
void *(*init)(void);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*recalc)(struct event_base *, void *, int);
int (*dispatch)(struct event_base *, void *, struct timeval *);
void (*dealloc)(void *);
};
#define TIMEOUT_DEFAULT {5, 0}
void *event_init(void);
int event_dispatch(void);
int event_base_dispatch(struct event_base *);
void event_base_free(struct event_base *);
#define _EVENT_LOG_DEBUG 0
#define _EVENT_LOG_MSG 1
#define _EVENT_LOG_WARN 2
#define _EVENT_LOG_ERR 3
typedef void (*event_log_cb)(int severity, const char *msg);
void event_set_log_callback(event_log_cb cb);
/* Associate a different event base with an event */
int event_base_set(struct event_base *, struct event *);
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
int event_loop(int);
int event_base_loop(struct event_base *, int);
int event_loopexit(struct timeval *); /* Causes the loop to exit */
int event_base_loopexit(struct event_base *, struct timeval *);
#define evtimer_add(ev, tv) event_add(ev, tv)
#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define evtimer_del(ev) event_del(ev)
#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define timeout_add(ev, tv) event_add(ev, tv)
#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define timeout_del(ev) event_del(ev)
#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define signal_add(ev, tv) event_add(ev, tv)
#define signal_set(ev, x, cb, arg) \
event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
#define signal_del(ev) event_del(ev)
#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv)
#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
int event_add(struct event *, struct timeval *);
int event_del(struct event *);
void event_active(struct event *, int, short);
int event_pending(struct event *, short, struct timeval *);
#ifdef WIN32
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT && (ev)->ev_fd != (int)INVALID_HANDLE_VALUE)
#else
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#endif
/* Some simple debugging functions */
const char *event_get_version(void);
const char *event_get_method(void);
/* These functions deal with event priorities */
int event_priority_init(int);
int event_base_priority_init(struct event_base *, int);
int event_priority_set(struct event *, int);
/* These functions deal with buffering input and output */
struct evbuffer {
u_char *buffer;
u_char *orig_buffer;
size_t misalign;
size_t totallen;
size_t off;
void (*cb)(struct evbuffer *, size_t, size_t, void *);
void *cbarg;
};
/* Just for error reporting - use other constants otherwise */
#define EVBUFFER_READ 0x01
#define EVBUFFER_WRITE 0x02
#define EVBUFFER_EOF 0x10
#define EVBUFFER_ERROR 0x20
#define EVBUFFER_TIMEOUT 0x40
struct bufferevent;
typedef void (*evbuffercb)(struct bufferevent *, void *);
typedef void (*everrorcb)(struct bufferevent *, short what, void *);
struct event_watermark {
size_t low;
size_t high;
};
struct bufferevent {
struct event ev_read;
struct event ev_write;
struct evbuffer *input;
struct evbuffer *output;
struct event_watermark wm_read;
struct event_watermark wm_write;
evbuffercb readcb;
evbuffercb writecb;
everrorcb errorcb;
void *cbarg;
int timeout_read; /* in seconds */
int timeout_write; /* in seconds */
short enabled; /* events that are currently enabled */
};
struct bufferevent *bufferevent_new(int fd,
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
void bufferevent_free(struct bufferevent *bufev);
int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_enable(struct bufferevent *bufev, short event);
int bufferevent_disable(struct bufferevent *bufev, short event);
void bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write);
#define EVBUFFER_LENGTH(x) (x)->off
#define EVBUFFER_DATA(x) (x)->buffer
#define EVBUFFER_INPUT(x) (x)->input
#define EVBUFFER_OUTPUT(x) (x)->output
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *);
int evbuffer_expand(struct evbuffer *, size_t);
int evbuffer_add(struct evbuffer *, const void *, size_t);
int evbuffer_remove(struct evbuffer *, void *, size_t);
char *evbuffer_readline(struct evbuffer *);
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
void evbuffer_drain(struct evbuffer *, size_t);
int evbuffer_write(struct evbuffer *, int);
int evbuffer_read(struct evbuffer *, int, int);
u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
/*
* Marshaling tagged data - We assume that all tags are inserted in their
* numeric order - so that unknown tags will always be higher than the
* known ones - and we can just ignore the end of an event buffer.
*/
void evtag_init(void);
void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data,
u_int32_t len);
void encode_int(struct evbuffer *evbuf, u_int32_t number);
void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag,
u_int32_t integer);
void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag,
const char *string);
void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag,
struct timeval *tv);
void evtag_test(void);
int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag,
struct evbuffer *dst);
int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag);
int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength);
int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength);
int evtag_consume(struct evbuffer *evbuf);
int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
u_int32_t *pinteger);
int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
size_t len);
int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
char **pstring);
int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
struct timeval *ptv);
#ifdef __cplusplus
}
#endif
#endif /* _EVENT_H_ */

View File

@ -0,0 +1,35 @@
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifndef _EVSIGNAL_H_
#define _EVSIGNAL_H_
void evsignal_init(void);
void evsignal_process(void);
int evsignal_add(struct event *);
int evsignal_del(struct event *);
#endif /* _EVSIGNAL_H_ */

View File

@ -0,0 +1,413 @@
/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/event.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
#define INTPTR(x) (intptr_t)x
#else
#define INTPTR(x) x
#endif
#include "event.h"
#include "log.h"
#define EVLIST_X_KQINKERNEL 0x1000
#define NEVENT 64
struct kqop {
struct kevent *changes;
int nchanges;
struct kevent *events;
int nevents;
int kq;
};
void *kq_init (void);
int kq_add (void *, struct event *);
int kq_del (void *, struct event *);
int kq_recalc (struct event_base *, void *, int);
int kq_dispatch (struct event_base *, void *, struct timeval *);
int kq_insert (struct kqop *, struct kevent *);
void kq_dealloc (void *);
const struct eventop kqops = {
"kqueue",
kq_init,
kq_add,
kq_del,
kq_recalc,
kq_dispatch,
kq_dealloc
};
void *
kq_init(void)
{
int kq;
struct kqop *kqueueop;
/* Disable kqueue when this environment variable is set */
if (getenv("EVENT_NOKQUEUE"))
return (NULL);
if (!(kqueueop = calloc(1, sizeof(struct kqop))))
return (NULL);
/* Initalize the kernel queue */
if ((kq = kqueue()) == -1) {
event_warn("kqueue");
free (kqueueop);
return (NULL);
}
kqueueop->kq = kq;
/* Initalize fields */
kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
if (kqueueop->changes == NULL) {
free (kqueueop);
return (NULL);
}
kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
if (kqueueop->events == NULL) {
free (kqueueop->changes);
free (kqueueop);
return (NULL);
}
kqueueop->nevents = NEVENT;
/* Check for Mac OS X kqueue bug. */
kqueueop->changes[0].ident = -1;
kqueueop->changes[0].filter = EVFILT_READ;
kqueueop->changes[0].flags = EV_ADD;
/*
* If kqueue works, then kevent will succeed, and it will
* stick an error in events[0]. If kqueue is broken, then
* kevent will fail.
*/
if (kevent(kq,
kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
kqueueop->events[0].ident != -1 ||
kqueueop->events[0].flags != EV_ERROR) {
event_warn("%s: detected broken kqueue; not using.", __func__);
free(kqueueop->changes);
free(kqueueop->events);
free(kqueueop);
close(kq);
return (NULL);
}
return (kqueueop);
}
int
kq_recalc(struct event_base *base, void *arg, int max)
{
return (0);
}
int
kq_insert(struct kqop *kqop, struct kevent *kev)
{
int nevents = kqop->nevents;
if (kqop->nchanges == nevents) {
struct kevent *newchange;
struct kevent *newresult;
nevents *= 2;
newchange = realloc(kqop->changes,
nevents * sizeof(struct kevent));
if (newchange == NULL) {
event_warn("%s: malloc", __func__);
return (-1);
}
kqop->changes = newchange;
newresult = realloc(kqop->events,
nevents * sizeof(struct kevent));
/*
* If we fail, we don't have to worry about freeing,
* the next realloc will pick it up.
*/
if (newresult == NULL) {
event_warn("%s: malloc", __func__);
return (-1);
}
kqop->events = newresult;
kqop->nevents = nevents;
}
memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
event_debug(("%s: fd %d %s%s",
__func__, kev->ident,
kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
kev->flags == EV_DELETE ? " (del)" : ""));
return (0);
}
static void
kq_sighandler(int sig)
{
/* Do nothing here */
}
int
kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
struct kqop *kqop = arg;
struct kevent *changes = kqop->changes;
struct kevent *events = kqop->events;
struct event *ev;
struct timespec ts;
int i, res;
TIMEVAL_TO_TIMESPEC(tv, &ts);
res = kevent(kqop->kq, changes, kqop->nchanges,
events, kqop->nevents, &ts);
kqop->nchanges = 0;
if (res == -1) {
if (errno != EINTR) {
event_warn("kevent");
return (-1);
}
return (0);
}
event_debug(("%s: kevent reports %d", __func__, res));
for (i = 0; i < res; i++) {
int which = 0;
if (events[i].flags & EV_ERROR) {
/*
* Error messages that can happen, when a delete fails.
* EBADF happens when the file discriptor has been
* closed,
* ENOENT when the file discriptor was closed and
* then reopened.
* EINVAL for some reasons not understood; EINVAL
* should not be returned ever; but FreeBSD does :-\
* An error is also indicated when a callback deletes
* an event we are still processing. In that case
* the data field is set to ENOENT.
*/
if (events[i].data == EBADF ||
events[i].data == EINVAL ||
events[i].data == ENOENT)
continue;
errno = events[i].data;
return (-1);
}
ev = (struct event *)events[i].udata;
if (events[i].filter == EVFILT_READ) {
which |= EV_READ;
} else if (events[i].filter == EVFILT_WRITE) {
which |= EV_WRITE;
} else if (events[i].filter == EVFILT_SIGNAL) {
which |= EV_SIGNAL;
}
if (!which)
continue;
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, which,
ev->ev_events & EV_SIGNAL ? events[i].data : 1);
}
return (0);
}
int
kq_add(void *arg, struct event *ev)
{
struct kqop *kqop = arg;
struct kevent kev;
if (ev->ev_events & EV_SIGNAL) {
int nsignal = EVENT_SIGNAL(ev);
memset(&kev, 0, sizeof(kev));
kev.ident = nsignal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, kq_sighandler) == SIG_ERR)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
return (0);
}
if (ev->ev_events & EV_READ) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_READ;
#ifdef NOTE_EOF
/* Make it behave like select() and poll() */
kev.fflags = NOTE_EOF;
#endif
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
}
if (ev->ev_events & EV_WRITE) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_WRITE;
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
}
return (0);
}
int
kq_del(void *arg, struct event *ev)
{
struct kqop *kqop = arg;
struct kevent kev;
if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
return (0);
if (ev->ev_events & EV_SIGNAL) {
int nsignal = EVENT_SIGNAL(ev);
memset(&kev, 0, sizeof(kev));
kev.ident = nsignal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, SIG_DFL) == SIG_ERR)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
return (0);
}
if (ev->ev_events & EV_READ) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_READ;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
}
if (ev->ev_events & EV_WRITE) {
memset(&kev, 0, sizeof(kev));
kev.ident = ev->ev_fd;
kev.filter = EVFILT_WRITE;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
}
return (0);
}
void
kq_dealloc(void *arg)
{
struct kqop *kqop = arg;
if (kqop->changes)
free(kqop->changes);
if (kqop->events)
free(kqop->events);
if (kqop->kq)
close(kqop->kq);
memset(kqop, 0, sizeof(struct kqop));
free(kqop);
}

219
contrib/pf/libevent/log.c Normal file
View File

@ -0,0 +1,219 @@
/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* log.c
*
* Based on err.c, which was adapted from OpenBSD libc *err* *warn* code.
*
* Copyright (c) 2005 Nick Mathewson <nickm@freehaven.net>
*
* Copyright (c) 2000 Dug Song <dugsong@monkey.org>
*
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include "misc.h"
#endif
#include <sys/types.h>
#include <sys/tree.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "event.h"
#include "log.h"
static void _warn_helper(int severity, int log_errno, const char *fmt,
va_list ap);
static void event_log(int severity, const char *msg);
static int
event_vsnprintf(char *str, size_t size, const char *format, va_list args)
{
int r;
if (size == 0)
return -1;
#ifdef WIN32
r = _vsnprintf(str, size, format, args);
#else
r = vsnprintf(str, size, format, args);
#endif
str[size-1] = '\0';
if (r < 0 || ((size_t)r) >= size) {
/* different platforms behave differently on overflow;
* handle both kinds. */
return -1;
}
return r;
}
static int
event_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int r;
va_start(ap, format);
r = event_vsnprintf(str, size, format, ap);
va_end(ap);
return r;
}
void
event_err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_ERR, errno, fmt, ap);
va_end(ap);
exit(eval);
}
void
event_warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_WARN, errno, fmt, ap);
va_end(ap);
}
void
event_errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_ERR, -1, fmt, ap);
va_end(ap);
exit(eval);
}
void
event_warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_WARN, -1, fmt, ap);
va_end(ap);
}
void
event_msgx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_MSG, -1, fmt, ap);
va_end(ap);
}
void
_event_debugx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap);
va_end(ap);
}
static void
_warn_helper(int severity, int log_errno, const char *fmt, va_list ap)
{
char buf[1024];
size_t len;
if (fmt != NULL)
event_vsnprintf(buf, sizeof(buf), fmt, ap);
else
buf[0] = '\0';
if (log_errno >= 0) {
len = strlen(buf);
if (len < sizeof(buf) - 3) {
event_snprintf(buf + len, sizeof(buf) - len, ": %s",
strerror(log_errno));
}
}
event_log(severity, buf);
}
static event_log_cb log_fn = NULL;
void
event_set_log_callback(event_log_cb cb)
{
log_fn = cb;
}
static void
event_log(int severity, const char *msg)
{
if (log_fn)
log_fn(severity, msg);
else {
const char *severity_str;
switch (severity) {
case _EVENT_LOG_DEBUG:
severity_str = "debug";
break;
case _EVENT_LOG_MSG:
severity_str = "msg";
break;
case _EVENT_LOG_WARN:
severity_str = "warn";
break;
case _EVENT_LOG_ERR:
severity_str = "err";
break;
default:
severity_str = "???";
break;
}
(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
}
}

43
contrib/pf/libevent/log.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifndef _LOG_H_
#define _LOG_H_
void event_err(int eval, const char *fmt, ...);
void event_warn(const char *fmt, ...);
void event_errx(int eval, const char *fmt, ...);
void event_warnx(const char *fmt, ...);
void event_msgx(const char *fmt, ...);
void _event_debugx(const char *fmt, ...);
#ifdef USE_DEBUG
#define event_debug(x) _event_debugx x
#else
#define event_debug(x) do {;} while (0)
#endif
#endif

388
contrib/pf/libevent/poll.c Normal file
View File

@ -0,0 +1,388 @@
/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/tree.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef CHECK_INVARIANTS
#include <assert.h>
#endif
#include "event.h"
#include "event-internal.h"
#include "evsignal.h"
#include "log.h"
extern volatile sig_atomic_t evsignal_caught;
struct pollop {
int event_count; /* Highest number alloc */
int nfds; /* Size of event_* */
int fd_count; /* Size of idxplus1_by_fd */
struct pollfd *event_set;
struct event **event_r_back;
struct event **event_w_back;
int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
* that 0 (which is easy to memset) can mean
* "no entry." */
};
void *poll_init (void);
int poll_add (void *, struct event *);
int poll_del (void *, struct event *);
int poll_recalc (struct event_base *, void *, int);
int poll_dispatch (struct event_base *, void *, struct timeval *);
void poll_dealloc (void *);
const struct eventop pollops = {
"poll",
poll_init,
poll_add,
poll_del,
poll_recalc,
poll_dispatch,
poll_dealloc
};
void *
poll_init(void)
{
struct pollop *pollop;
/* Disable poll when this environment variable is set */
if (getenv("EVENT_NOPOLL"))
return (NULL);
if (!(pollop = calloc(1, sizeof(struct pollop))))
return (NULL);
evsignal_init();
return (pollop);
}
/*
* Called with the highest fd that we know about. If it is 0, completely
* recalculate everything.
*/
int
poll_recalc(struct event_base *base, void *arg, int max)
{
return (0);
}
#ifdef CHECK_INVARIANTS
static void
poll_check_ok(struct pollop *pop)
{
int i, idx;
struct event *ev;
for (i = 0; i < pop->fd_count; ++i) {
idx = pop->idxplus1_by_fd[i]-1;
if (idx < 0)
continue;
assert(pop->event_set[idx].fd == i);
if (pop->event_set[idx].events & POLLIN) {
ev = pop->event_r_back[idx];
assert(ev);
assert(ev->ev_events & EV_READ);
assert(ev->ev_fd == i);
}
if (pop->event_set[idx].events & POLLOUT) {
ev = pop->event_w_back[idx];
assert(ev);
assert(ev->ev_events & EV_WRITE);
assert(ev->ev_fd == i);
}
}
for (i = 0; i < pop->nfds; ++i) {
struct pollfd *pfd = &pop->event_set[i];
assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
}
}
#else
#define poll_check_ok(pop)
#endif
int
poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
int res, i, sec, nfds;
struct pollop *pop = arg;
poll_check_ok(pop);
sec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
nfds = pop->nfds;
res = poll(pop->event_set, nfds, sec);
if (res == -1) {
if (errno != EINTR) {
event_warn("poll");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
event_debug(("%s: poll reports %d", __func__, res));
if (res == 0)
return (0);
for (i = 0; i < nfds; i++) {
int what = pop->event_set[i].revents;
struct event *r_ev = NULL, *w_ev = NULL;
if (!what)
continue;
res = 0;
/* If the file gets closed notify */
if (what & (POLLHUP|POLLERR))
what |= POLLIN|POLLOUT;
if (what & POLLIN) {
res |= EV_READ;
r_ev = pop->event_r_back[i];
}
if (what & POLLOUT) {
res |= EV_WRITE;
w_ev = pop->event_w_back[i];
}
if (res == 0)
continue;
if (r_ev && (res & r_ev->ev_events)) {
if (!(r_ev->ev_events & EV_PERSIST))
event_del(r_ev);
event_active(r_ev, res & r_ev->ev_events, 1);
}
if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
if (!(w_ev->ev_events & EV_PERSIST))
event_del(w_ev);
event_active(w_ev, res & w_ev->ev_events, 1);
}
}
return (0);
}
int
poll_add(void *arg, struct event *ev)
{
struct pollop *pop = arg;
struct pollfd *pfd = NULL;
int i;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(ev));
if (!(ev->ev_events & (EV_READ|EV_WRITE)))
return (0);
poll_check_ok(pop);
if (pop->nfds + 1 >= pop->event_count) {
struct pollfd *tmp_event_set;
struct event **tmp_event_r_back;
struct event **tmp_event_w_back;
int tmp_event_count;
if (pop->event_count < 32)
tmp_event_count = 32;
else
tmp_event_count = pop->event_count * 2;
/* We need more file descriptors */
tmp_event_set = realloc(pop->event_set,
tmp_event_count * sizeof(struct pollfd));
if (tmp_event_set == NULL) {
event_warn("realloc");
return (-1);
}
pop->event_set = tmp_event_set;
tmp_event_r_back = realloc(pop->event_r_back,
tmp_event_count * sizeof(struct event *));
if (tmp_event_r_back == NULL) {
/* event_set overallocated; that's okay. */
event_warn("realloc");
return (-1);
}
pop->event_r_back = tmp_event_r_back;
tmp_event_w_back = realloc(pop->event_w_back,
tmp_event_count * sizeof(struct event *));
if (tmp_event_w_back == NULL) {
/* event_set and event_r_back overallocated; that's
* okay. */
event_warn("realloc");
return (-1);
}
pop->event_w_back = tmp_event_w_back;
pop->event_count = tmp_event_count;
}
if (ev->ev_fd >= pop->fd_count) {
int *tmp_idxplus1_by_fd;
int new_count;
if (pop->fd_count < 32)
new_count = 32;
else
new_count = pop->fd_count * 2;
while (new_count <= ev->ev_fd)
new_count *= 2;
tmp_idxplus1_by_fd =
realloc(pop->idxplus1_by_fd, new_count * sizeof(int));
if (tmp_idxplus1_by_fd == NULL) {
event_warn("realloc");
return (-1);
}
pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
memset(pop->idxplus1_by_fd + pop->fd_count,
0, sizeof(int)*(new_count - pop->fd_count));
pop->fd_count = new_count;
}
i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
if (i >= 0) {
pfd = &pop->event_set[i];
} else {
i = pop->nfds++;
pfd = &pop->event_set[i];
pfd->events = 0;
pfd->fd = ev->ev_fd;
pop->event_w_back[i] = pop->event_r_back[i] = NULL;
pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
}
pfd->revents = 0;
if (ev->ev_events & EV_WRITE) {
pfd->events |= POLLOUT;
pop->event_w_back[i] = ev;
}
if (ev->ev_events & EV_READ) {
pfd->events |= POLLIN;
pop->event_r_back[i] = ev;
}
poll_check_ok(pop);
return (0);
}
/*
* Nothing to be done here.
*/
int
poll_del(void *arg, struct event *ev)
{
struct pollop *pop = arg;
struct pollfd *pfd = NULL;
int i;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_del(ev));
if (!(ev->ev_events & (EV_READ|EV_WRITE)))
return (0);
poll_check_ok(pop);
i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
if (i < 0)
return (-1);
/* Do we still want to read or write? */
pfd = &pop->event_set[i];
if (ev->ev_events & EV_READ) {
pfd->events &= ~POLLIN;
pop->event_r_back[i] = NULL;
}
if (ev->ev_events & EV_WRITE) {
pfd->events &= ~POLLOUT;
pop->event_w_back[i] = NULL;
}
poll_check_ok(pop);
if (pfd->events)
/* Another event cares about that fd. */
return (0);
/* Okay, so we aren't interested in that fd anymore. */
pop->idxplus1_by_fd[ev->ev_fd] = 0;
--pop->nfds;
if (i != pop->nfds) {
/*
* Shift the last pollfd down into the now-unoccupied
* position.
*/
memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
sizeof(struct pollfd));
pop->event_r_back[i] = pop->event_r_back[pop->nfds];
pop->event_w_back[i] = pop->event_w_back[pop->nfds];
pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
}
poll_check_ok(pop);
return (0);
}
void
poll_dealloc(void *arg)
{
struct pollop *pop = arg;
if (pop->event_set)
free(pop->event_set);
if (pop->event_r_back)
free(pop->event_r_back);
if (pop->event_w_back)
free(pop->event_w_back);
if (pop->idxplus1_by_fd)
free(pop->idxplus1_by_fd);
memset(pop, 0, sizeof(struct pollop));
free(pop);
}

View File

@ -0,0 +1,370 @@
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/tree.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef CHECK_INVARIANTS
#include <assert.h>
#endif
#include "event.h"
#include "event-internal.h"
#include "evsignal.h"
#include "log.h"
#ifndef howmany
#define howmany(x, y) (((x)+((y)-1))/(y))
#endif
extern volatile sig_atomic_t evsignal_caught;
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
fd_set *event_readset_in;
fd_set *event_writeset_in;
fd_set *event_readset_out;
fd_set *event_writeset_out;
struct event **event_r_by_fd;
struct event **event_w_by_fd;
};
void *select_init (void);
int select_add (void *, struct event *);
int select_del (void *, struct event *);
int select_recalc (struct event_base *, void *, int);
int select_dispatch (struct event_base *, void *, struct timeval *);
void select_dealloc (void *);
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_recalc,
select_dispatch,
select_dealloc
};
static int select_resize(struct selectop *sop, int fdsz);
void *
select_init(void)
{
struct selectop *sop;
/* Disable select when this environment variable is set */
if (getenv("EVENT_NOSELECT"))
return (NULL);
if (!(sop = calloc(1, sizeof(struct selectop))))
return (NULL);
select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask));
evsignal_init();
return (sop);
}
#ifdef CHECK_INVARIANTS
static void
check_selectop(struct selectop *sop)
{
int i;
for (i=0;i<=sop->event_fds;++i) {
if (FD_ISSET(i, sop->event_readset_in)) {
assert(sop->event_r_by_fd[i]);
assert(sop->event_r_by_fd[i]->ev_events & EV_READ);
assert(sop->event_r_by_fd[i]->ev_fd == i);
} else {
assert(! sop->event_r_by_fd[i]);
}
if (FD_ISSET(i, sop->event_writeset_in)) {
assert(sop->event_w_by_fd[i]);
assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE);
assert(sop->event_w_by_fd[i]->ev_fd == i);
} else {
assert(! sop->event_w_by_fd[i]);
}
}
}
#else
#define check_selectop(sop) do { (void) sop; } while (0)
#endif
/*
* Called with the highest fd that we know about. If it is 0, completely
* recalculate everything.
*/
int
select_recalc(struct event_base *base, void *arg, int max)
{
struct selectop *sop = arg;
check_selectop(sop);
return (0);
}
int
select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
int res, i;
struct selectop *sop = arg;
check_selectop(sop);
memcpy(sop->event_readset_out, sop->event_readset_in,
sop->event_fdsz);
memcpy(sop->event_writeset_out, sop->event_writeset_in,
sop->event_fdsz);
res = select(sop->event_fds + 1, sop->event_readset_out,
sop->event_writeset_out, NULL, tv);
check_selectop(sop);
if (res == -1) {
if (errno != EINTR) {
event_warn("select");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
event_debug(("%s: select reports %d", __func__, res));
check_selectop(sop);
for (i = 0; i <= sop->event_fds; ++i) {
struct event *r_ev = NULL, *w_ev = NULL;
res = 0;
if (FD_ISSET(i, sop->event_readset_out)) {
r_ev = sop->event_r_by_fd[i];
res |= EV_READ;
}
if (FD_ISSET(i, sop->event_writeset_out)) {
w_ev = sop->event_w_by_fd[i];
res |= EV_WRITE;
}
if (r_ev && (res & r_ev->ev_events)) {
if (!(r_ev->ev_events & EV_PERSIST))
event_del(r_ev);
event_active(r_ev, res & r_ev->ev_events, 1);
}
if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
if (!(w_ev->ev_events & EV_PERSIST))
event_del(w_ev);
event_active(w_ev, res & w_ev->ev_events, 1);
}
}
check_selectop(sop);
return (0);
}
static int
select_resize(struct selectop *sop, int fdsz)
{
int n_events, n_events_old;
fd_set *readset_in = NULL;
fd_set *writeset_in = NULL;
fd_set *readset_out = NULL;
fd_set *writeset_out = NULL;
struct event **r_by_fd = NULL;
struct event **w_by_fd = NULL;
n_events = (fdsz/sizeof(fd_mask)) * NFDBITS;
n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS;
if (sop->event_readset_in)
check_selectop(sop);
if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL)
goto error;
sop->event_readset_in = readset_in;
if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL)
goto error;
sop->event_readset_out = readset_out;
if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL)
goto error;
sop->event_writeset_in = writeset_in;
if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL)
goto error;
sop->event_writeset_out = writeset_out;
if ((r_by_fd = realloc(sop->event_r_by_fd,
n_events*sizeof(struct event*))) == NULL)
goto error;
sop->event_r_by_fd = r_by_fd;
if ((w_by_fd = realloc(sop->event_w_by_fd,
n_events * sizeof(struct event*))) == NULL)
goto error;
sop->event_w_by_fd = w_by_fd;
memset((char *)sop->event_readset_in + sop->event_fdsz, 0,
fdsz - sop->event_fdsz);
memset((char *)sop->event_writeset_in + sop->event_fdsz, 0,
fdsz - sop->event_fdsz);
memset(sop->event_r_by_fd + n_events_old, 0,
(n_events-n_events_old) * sizeof(struct event*));
memset(sop->event_w_by_fd + n_events_old, 0,
(n_events-n_events_old) * sizeof(struct event*));
sop->event_fdsz = fdsz;
check_selectop(sop);
return (0);
error:
event_warn("malloc");
return (-1);
}
int
select_add(void *arg, struct event *ev)
{
struct selectop *sop = arg;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(ev));
check_selectop(sop);
/*
* Keep track of the highest fd, so that we can calculate the size
* of the fd_sets for select(2)
*/
if (sop->event_fds < ev->ev_fd) {
int fdsz = sop->event_fdsz;
if (fdsz < sizeof(fd_mask))
fdsz = sizeof(fd_mask);
while (fdsz <
(howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask)))
fdsz *= 2;
if (fdsz != sop->event_fdsz) {
if (select_resize(sop, fdsz)) {
check_selectop(sop);
return (-1);
}
}
sop->event_fds = ev->ev_fd;
}
if (ev->ev_events & EV_READ) {
FD_SET(ev->ev_fd, sop->event_readset_in);
sop->event_r_by_fd[ev->ev_fd] = ev;
}
if (ev->ev_events & EV_WRITE) {
FD_SET(ev->ev_fd, sop->event_writeset_in);
sop->event_w_by_fd[ev->ev_fd] = ev;
}
check_selectop(sop);
return (0);
}
/*
* Nothing to be done here.
*/
int
select_del(void *arg, struct event *ev)
{
struct selectop *sop = arg;
check_selectop(sop);
if (ev->ev_events & EV_SIGNAL)
return (evsignal_del(ev));
if (sop->event_fds < ev->ev_fd) {
check_selectop(sop);
return (0);
}
if (ev->ev_events & EV_READ) {
FD_CLR(ev->ev_fd, sop->event_readset_in);
sop->event_r_by_fd[ev->ev_fd] = NULL;
}
if (ev->ev_events & EV_WRITE) {
FD_CLR(ev->ev_fd, sop->event_writeset_in);
sop->event_w_by_fd[ev->ev_fd] = NULL;
}
check_selectop(sop);
return (0);
}
void
select_dealloc(void *arg)
{
struct selectop *sop = arg;
if (sop->event_readset_in)
free(sop->event_readset_in);
if (sop->event_writeset_in)
free(sop->event_writeset_in);
if (sop->event_readset_out)
free(sop->event_readset_out);
if (sop->event_writeset_out)
free(sop->event_writeset_out);
if (sop->event_r_by_fd)
free(sop->event_r_by_fd);
if (sop->event_w_by_fd)
free(sop->event_w_by_fd);
memset(sop, 0, sizeof(struct selectop));
free(sop);
}

View File

@ -0,0 +1,180 @@
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <sys/socket.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "event.h"
#include "evsignal.h"
#include "log.h"
extern struct event_list signalqueue;
static sig_atomic_t evsigcaught[NSIG];
volatile sig_atomic_t evsignal_caught = 0;
static struct event ev_signal;
static int ev_signal_pair[2];
static int ev_signal_added;
static void evsignal_handler(int sig);
/* Callback for when the signal handler write a byte to our signaling socket */
static void
evsignal_cb(int fd, short what, void *arg)
{
static char signals[100];
struct event *ev = arg;
ssize_t n;
n = read(fd, signals, sizeof(signals));
if (n == -1)
event_err(1, "%s: read", __func__);
event_add(ev, NULL);
}
#ifdef HAVE_SETFD
#define FD_CLOSEONEXEC(x) do { \
if (fcntl(x, F_SETFD, 1) == -1) \
event_warn("fcntl(%d, F_SETFD)", x); \
} while (0)
#else
#define FD_CLOSEONEXEC(x)
#endif
void
evsignal_init(void)
{
/*
* Our signal handler is going to write to one end of the socket
* pair to wake up our event loop. The event loop then scans for
* signals that got delivered.
*/
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ev_signal_pair) == -1)
event_err(1, "%s: socketpair", __func__);
FD_CLOSEONEXEC(ev_signal_pair[0]);
FD_CLOSEONEXEC(ev_signal_pair[1]);
fcntl(ev_signal_pair[0], F_SETFL, O_NONBLOCK);
event_set(&ev_signal, ev_signal_pair[1], EV_READ,
evsignal_cb, &ev_signal);
ev_signal.ev_flags |= EVLIST_INTERNAL;
}
int
evsignal_add(struct event *ev)
{
int evsignal;
struct sigaction sa;
if (ev->ev_events & (EV_READ|EV_WRITE))
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = evsignal_handler;
sigfillset(&sa.sa_mask);
sa.sa_flags |= SA_RESTART;
if (sigaction(evsignal, &sa, NULL) == -1)
return (-1);
if (!ev_signal_added) {
ev_signal_added = 1;
event_add(&ev_signal, NULL);
}
return (0);
}
/*
* Nothing to be done here.
*/
int
evsignal_del(struct event *ev)
{
int evsignal;
evsignal = EVENT_SIGNAL(ev);
return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
}
static void
evsignal_handler(int sig)
{
int save_errno = errno;
evsigcaught[sig]++;
evsignal_caught = 1;
/* Wake up our notification mechanism */
write(ev_signal_pair[0], "a", 1);
errno = save_errno;
}
void
evsignal_process(void)
{
struct event *ev;
sig_atomic_t ncalls;
evsignal_caught = 0;
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
ncalls = evsigcaught[EVENT_SIGNAL(ev)];
if (ncalls) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, EV_SIGNAL, ncalls);
evsigcaught[EVENT_SIGNAL(ev)] = 0;
}
}
}

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pf.4,v 1.54 2004/12/22 17:17:55 dhartmei Exp $
.\" $OpenBSD: pf.4,v 1.58 2007/02/09 11:39:06 henning Exp $
.\"
.\" Copyright (C) 2001, Kjell Wooding. All rights reserved.
.\"
@ -184,6 +184,11 @@ using the
obtained through a preceding
.Dv DIOCGETRULES
call.
If
.Va action
is set to
.Dv PF_GET_CLR_CNTR ,
the per-rule statistics on the requested rule are cleared.
.It Dv DIOCGETADDRS Fa "struct pfioc_pooladdr *pp"
Get a
.Va ticket
@ -346,6 +351,7 @@ struct pf_status {
u_int32_t debug;
u_int32_t hostid;
char ifname[IFNAMSIZ];
u_int8_t pf_chksum[MD5_DIGEST_LENGTH];
};
.Ed
.It Dv DIOCCLRSTATUS
@ -389,19 +395,14 @@ struct pfioc_states {
.Pp
If
.Va ps_len
is zero, all states will be gathered into
.Va pf_states
and
is non-zero on entry, as many states as possible that can fit into this
size will be copied into the supplied buffer
.Va ps_states .
On exit,
.Va ps_len
will be set to the size they take in memory (i.e.,
is always set to the total size required to hold all state table entries
(i.e., it is set to
.Li sizeof(struct pf_state) * nr ) .
If
.Va ps_len
is non-zero, as many states that can fit into
.Va ps_len
as possible will be gathered, and
.Va ps_len
will be updated to the size those rules take in memory.
.It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr"
Add or remove the
.Va rule
@ -483,7 +484,8 @@ struct pfioc_limit {
unsigned limit;
};
enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS };
enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS,
PF_LIMIT_TABLES, PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX };
.Ed
.It Dv DIOCGETLIMIT Fa "struct pfioc_limit *pl"
Get the hard
@ -521,10 +523,15 @@ struct pfioc_table {
.It Dv DIOCRADDTABLES Fa "struct pfioc_table *io"
Create one or more tables.
On entry,
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_table
structures.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_table
containing at least
.Vt pfrio_size
elements.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_table .
On exit,
.Va pfrio_nadd
contains the number of tables effectively created.
@ -539,12 +546,17 @@ struct pfr_table {
.It Dv DIOCRDELTABLES Fa "struct pfioc_table *io"
Delete one or more tables.
On entry,
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_table
structures.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_table
containing at least
.Vt pfrio_size
elements.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_table .
On exit,
.Va pfrio_nadd
.Va pfrio_ndel
contains the number of tables effectively deleted.
.It Dv DIOCRGETTABLES Fa "struct pfioc_table *io"
Get the list of all tables.
@ -583,10 +595,15 @@ struct pfr_tstats {
.It Dv DIOCRCLRTSTATS Fa "struct pfioc_table *io"
Clear the statistics of one or more tables.
On entry,
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_table
structures.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_table
containing at least
.Vt pfrio_size
elements.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_table .
On exit,
.Va pfrio_nzero
contains the number of tables effectively cleared.
@ -603,10 +620,15 @@ Add one or more addresses to a table.
On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains the list of
.Vt pfr_addr
structures to add.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_addr
containing at least
.Vt pfrio_size
elements to add to the table.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_addr .
On exit,
.Va pfrio_nadd
contains the number of addresses effectively added.
@ -629,10 +651,15 @@ Delete one or more addresses from a table.
On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains the list of
.Vt pfr_addr
structures to delete.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_addr
containing at least
.Vt pfrio_size
elements to delete from the table.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_addr .
On exit,
.Va pfrio_ndel
contains the number of addresses effectively deleted.
@ -643,10 +670,15 @@ This is the most complicated command, which uses all the structure members.
On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains the new list of
.Vt pfr_addr
structures.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_addr
containing at least
.Vt pfrio_size
elements which become the new contents of the table.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_addr .
Additionally, if
.Va pfrio_size2
is non-zero,
@ -701,10 +733,15 @@ Clear the statistics of one or more addresses.
On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_addr
structures to clear.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_addr
containing at least
.Vt pfrio_size
elements to be cleared from the table.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_addr .
On exit,
.Va pfrio_nzero
contains the number of addresses effectively cleared.
@ -713,13 +750,18 @@ Test if the given addresses match a table.
On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_addr
structures to test.
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_addr
containing at least
.Vt pfrio_size
elements, each of which will be tested for a match in the table.
.Vt pfrio_esize
must be the size of
.Vt struct pfr_addr .
On exit, the kernel updates the
.Vt pfr_addr
table by setting the
array by setting the
.Va pfra_fback
member appropriately.
.It Dv DIOCRSETTFLAGS Fa "struct pfioc_table *io"
@ -729,14 +771,19 @@ or
.Dv PFR_TFLAG_PERSIST
flags of a table.
On entry,
.Va pfrio_buffer[pfrio_size]
contains a table of
.Vt pfr_table
structures, and
.Va pfrio_buffer
must point to an array of
.Vt struct pfr_table
containing at least
.Vt pfrio_size
elements.
.Va pfrio_esize
must be the size of
.Vt struct pfr_table .
.Va pfrio_setflag
contains the flags to add, while
must contain the flags to add, while
.Va pfrio_clrflag
contains the flags to remove.
must contain the flags to remove.
On exit,
.Va pfrio_nchange
and
@ -751,7 +798,7 @@ On entry,
.Va pfrio_table
contains the table ID and
.Va pfrio_buffer[pfrio_size]
contains the list of
contains an array of
.Vt pfr_addr
structures to put in the table.
A valid ticket must also be supplied to
@ -953,10 +1000,6 @@ struct pfioc_iface {
int pfiio_nzero;
int pfiio_flags;
};
#define PFI_FLAG_GROUP 0x0001 /* gets groups of interfaces */
#define PFI_FLAG_INSTANCE 0x0002 /* gets single interfaces */
#define PFI_FLAG_ALLMASK 0x0003
.Ed
.Pp
If not empty,
@ -966,61 +1009,45 @@ can be used to restrict the search to a specific interface or driver.
is the user-supplied buffer for returning the data.
On entry,
.Va pfiio_size
represents the number of
.Va pfi_if
contains the number of
.Vt pfi_kif
entries that can fit into the buffer.
The kernel will replace this value by the real number of entries it wants
to return.
.Va pfiio_esize
should be set to
.Li sizeof(struct pfi_if) .
.Va pfiio_flags
should be set to
.Dv PFI_FLAG_GROUP ,
.Dv PFI_FLAG_INSTANCE ,
or both, to tell the kernel to return a group of interfaces
(drivers, like "fxp"), real interface instances (like "fxp1") or both.
.Li sizeof(struct pfi_kif) .
.Pp
The data is returned in the
.Vt pfi_if
.Vt pfi_kif
structure described below:
.Bd -literal
struct pfi_if {
char pfif_name[IFNAMSIZ];
u_int64_t pfif_packets[2][2][2];
u_int64_t pfif_bytes[2][2][2];
u_int64_t pfif_addcnt;
u_int64_t pfif_delcnt;
long pfif_tzero;
int pfif_states;
int pfif_rules;
int pfif_flags;
struct pfi_kif {
RB_ENTRY(pfi_kif) pfik_tree;
char pfik_name[IFNAMSIZ];
u_int64_t pfik_packets[2][2][2];
u_int64_t pfik_bytes[2][2][2];
u_int32_t pfik_tzero;
int pfik_flags;
struct pf_state_tree_lan_ext pfik_lan_ext;
struct pf_state_tree_ext_gwy pfik_ext_gwy;
TAILQ_ENTRY(pfi_kif) pfik_w_states;
void *pfik_ah_cookie;
struct ifnet *pfik_ifp;
struct ifg_group *pfik_group;
int pfik_states;
int pfik_rules;
TAILQ_HEAD(, pfi_dynaddr) pfik_dynaddrs;
};
#define PFI_IFLAG_GROUP 0x0001 /* group of interfaces */
#define PFI_IFLAG_INSTANCE 0x0002 /* single instance */
#define PFI_IFLAG_CLONABLE 0x0010 /* clonable group */
#define PFI_IFLAG_DYNAMIC 0x0020 /* dynamic group */
#define PFI_IFLAG_ATTACHED 0x0040 /* interface attached */
.Ed
.It Dv DIOCICLRISTATS Fa "struct pfioc_iface *io"
Clear the statistics counters of one or more interfaces.
.Va pfiio_name
and
.Va pfiio_flags
can be used to select which interfaces need to be cleared.
The filtering process is the same as for
.Dv DIOCIGETIFACES .
.Va pfiio_nzero
will be set by the kernel to the number of interfaces and drivers
that have been cleared.
.It Dv DIOCSETIFFLAG Fa "struct pfioc_iface *io"
Set the user setable flags (described below) of the pf internal interface
description.
Set the user setable flags (described above) of the
.Nm
internal interface description.
The filtering process is the same as for
.Dv DIOCIGETIFACES .
.Bd -literal
#define PFI_IFLAG_SKIP 0x0100 /* skip interface */
#define PFI_IFLAG_SETABLE_MASK 0x0100 /* mask */
#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */
.Ed
.It Dv DIOCCLRIFFLAG Fa "struct pfioc_iface *io"
Works as

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pf.os.5,v 1.6 2004/03/31 11:13:03 dhartmei Exp $
.\" $OpenBSD: pf.os.5,v 1.7 2005/11/16 20:07:18 stevesk Exp $
.\"
.\" Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
.\"
@ -204,37 +204,15 @@ The
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
03:13:48.118526 10.0.0.1.3377 > 10.0.0.2.80: S [tcp sum ok] \e
534596083:534596083(0) win 57344 <mss 1460> (DF) [tos 0x10] \e
(ttl 64, id 11315)
(ttl 64, id 11315, len 44)
.Ed
.Pp
almost translates into the following fingerprint
.Bd -literal
57344:64:1:44:M1460: exampleOS:1.0::exampleOS 1.0
.Ed
.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 ,

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pflog.4,v 1.7 2004/03/21 19:47:59 miod Exp $
.\" $OpenBSD: pflog.4,v 1.9 2006/10/25 12:51:31 jmc Exp $
.\"
.\" Copyright (c) 2001 Tobias Weingartner
.\" All rights reserved.
@ -45,6 +45,14 @@ on the
interface, or stored to disk using
.Xr pflogd 8 .
.Pp
The pflog0 interface is created automatically at boot if both
.Xr pf 4
and
.Xr pflogd 8
are enabled;
further instances can be created using
.Xr ifconfig 8 .
.Pp
Each packet retrieved on this interface has a header associated
with it of length
.Dv PFLOG_HDRLEN .
@ -63,14 +71,22 @@ struct pfloghdr {
char ruleset[PF_RULESET_NAME_SIZE];
u_int32_t rulenr;
u_int32_t subrulenr;
uid_t uid;
pid_t pid;
uid_t rule_uid;
pid_t rule_pid;
u_int8_t dir;
u_int8_t pad[3];
};
.Ed
.Sh EXAMPLES
Create a
.Nm
interface
and monitor all packets logged on it:
.Bd -literal -offset indent
# ifconfig pflog0 up
# tcpdump -n -e -ttt -i pflog0
# ifconfig pflog1 up
# tcpdump -n -e -ttt -i pflog1
.Ed
.Sh SEE ALSO
.Xr inet 4 ,

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pfsync.4,v 1.22 2005/02/24 15:53:17 jmc Exp $
.\" $OpenBSD: pfsync.4,v 1.24 2006/10/23 07:05:49 jmc Exp $
.\"
.\" Copyright (c) 2002 Michael Shalayeff
.\" Copyright (c) 2003-2004 Ryan McBride
@ -200,7 +200,7 @@ The following should be added to the top of
.Pa /etc/pf.conf :
.Bd -literal -offset indent
pass quick on { sis2 } proto pfsync
pass on { sis0 sis1 } proto carp keep state
pass on { sis0 sis1 } proto carp
.Ed
.Pp
If it is preferable that one firewall handle the traffic,
@ -236,6 +236,7 @@ net.inet.carp.preempt=1
.Xr pf.conf 5 ,
.Xr protocols 5 ,
.Xr ifconfig 8 ,
.Xr ifstated 8 ,
.Xr tcpdump 8
.Sh HISTORY
The

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pf_print_state.c,v 1.40 2004/12/10 22:13:26 henning Exp $ */
/* $OpenBSD: pf_print_state.c,v 1.44 2007/03/01 17:20:53 deraadt Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@ -96,6 +96,9 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
case PF_ADDR_NOROUTE:
printf("no-route");
return;
case PF_ADDR_URPFFAILED:
printf("urpf-failed");
return;
case PF_ADDR_RTLABEL:
printf("route \"%s\"", addr->v.rtlabelname);
return;
@ -274,7 +277,7 @@ print_state(struct pf_state *s, int opts)
min = s->expire % 60;
s->expire /= 60;
printf(", expires in %.2u:%.2u:%.2u", s->expire, min, sec);
printf(", %u:%u pkts, %u:%u bytes",
printf(", %llu:%llu pkts, %llu:%llu bytes",
s->packets[0], s->packets[1], s->bytes[0], s->bytes[1]);
if (s->anchor.nr != -1)
printf(", anchor %u", s->anchor.nr);
@ -287,8 +290,9 @@ print_state(struct pf_state *s, int opts)
printf("\n");
}
if (opts & PF_OPT_VERBOSE2) {
printf(" id: %016llx creatorid: %08x\n",
betoh64(s->id), ntohl(s->creatorid));
printf(" id: %016llx creatorid: %08x%s\n",
betoh64(s->id), ntohl(s->creatorid),
((s->sync_flags & PFSTATE_NOSYNC) ? " (no-sync)" : ""));
}
}

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pfctl.8,v 1.118 2005/01/05 23:41:45 jmc Exp $
.\" $OpenBSD: pfctl.8,v 1.128 2007/01/30 21:01:56 jmc Exp $
.\"
.\" Copyright (c) 2001 Kjell Wooding. All rights reserved.
.\"
@ -33,23 +33,23 @@
.Sh SYNOPSIS
.Nm pfctl
.Bk -words
.Op Fl AdeghmNnOoqRrvz
.Op Fl AdeghmNnOqRrvz
.Op Fl a Ar anchor
.Xo
.Oo Fl D
.Ar macro Ns = Ns Ar value Oc
.Xc
.Oo Fl D Ar macro Ns =
.Ar value Oc
.Op Fl F Ar modifier
.Op Fl f Ar file
.Op Fl i Ar interface
.Op Fl k Ar host
.Op Fl K Ar host | network
.Op Fl k Ar host | network
.Op Fl o Op Ar level
.Op Fl p Ar device
.Op Fl s Ar modifier
.Oo Xo
.Oo
.Fl t Ar table
.Fl T Ar command
.Op Ar address ... Oc
.Xc
.Op Ar address ...
.Oc
.Op Fl x Ar level
.Ek
.Sh DESCRIPTION
@ -138,8 +138,10 @@ rules from the main ruleset is described in
For example, the following will show all filter rules (see the
.Fl s
flag below) inside the anchor
.Li authpf/smith(1234) ,
which would have been created for user smith by
.Dq authpf/smith(1234) ,
which would have been created for user
.Dq smith
by
.Xr authpf 8 ,
PID 1234:
.Bd -literal -offset indent
@ -161,6 +163,27 @@ This is similar to C rules for variable scope.
It is possible to create distinct tables with the same name in the global
ruleset and in an anchor, but this is often bad design and a warning will be
issued in that case.
.Pp
By default, recursive inline printing of anchors applies only to unnamed
anchors specified inline in the ruleset.
If the anchor name is terminated with a
.Sq *
character, the
.Fl s
flag will recursively print all anchors in a brace delimited block.
For example the following will print the
.Dq authpf
ruleset recursively:
.Bd -literal -offset indent
# pfctl -a 'authpf/*' -sr
.Ed
.Pp
To print the main ruleset recursively, specify only
.Sq *
as the anchor name:
.Bd -literal -offset indent
# pfctl -a '*' -sr
.Ed
.It Fl D Ar macro Ns = Ns Ar value
Define
.Ar macro
@ -215,29 +238,49 @@ Help.
.It Fl i Ar interface
Restrict the operation to the given
.Ar interface .
.It Fl k Ar host
.It Fl K Ar host | network
Kill all of the source tracking entries originating from the specified
.Ar host
or
.Ar network .
A second
.Fl K Ar host
or
.Fl K Ar network
option may be specified, which will kill all the source tracking
entries from the first host/network to the second.
.It Fl k Ar host | network
Kill all of the state entries originating from the specified
.Ar host .
.Ar host
or
.Ar network .
A second
.Fl k Ar host
or
.Fl k Ar network
option may be specified, which will kill all the state entries
from the first
.Ar host
to the second
.Ar host .
from the first host/network to the second.
For example, to kill all of the state entries originating from
.Li host :
.Bd -literal -offset indent
# pfctl -k host
.Ed
.Dq host :
.Pp
.Dl # pfctl -k host
.Pp
To kill all of the state entries from
.Li host1
.Dq host1
to
.Li host2 :
.Bd -literal -offset indent
# pfctl -k host1 -k host2
.Ed
.Dq host2 :
.Pp
.Dl # pfctl -k host1 -k host2
.Pp
To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16:
.Pp
.Dl # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16
.Pp
A network prefix length of 0 can be used as a wildcard.
To kill all states with the target
.Dq host2 :
.Pp
.Dl # pfctl -k 0.0.0.0/0 -k host2
.It Fl m
Merge in explicitly given options without resetting those
which are omitted.
@ -253,11 +296,22 @@ Do not actually load rules, just parse them.
.It Fl O
Load only the options present in the rule file.
Other rules and options are ignored.
.It Fl o
Enable the ruleset optimizer.
.It Fl o Op Ar level
Control the ruleset optimizer.
The ruleset optimizer attempts to improve rulesets by removing rule
duplication and making better use of rule ordering.
Specifically, it does four things:
.Pp
.Bl -tag -width xxxxxxxxxxxx -compact
.It Fl o Cm none
Disable the ruleset optimizer.
.It Fl o Cm basic
Enable basic ruleset optimizations.
.It Fl o Cm profile
Enable basic ruleset optimizations with profiling.
.El
.Pp
.Cm basic
optimization does does four things:
.Pp
.Bl -enum -compact
.It
@ -270,10 +324,10 @@ combine multiple rules into a table when advantageous
re-order the rules to improve evaluation performance
.El
.Pp
A second
.Fl o
may be specified to use the currently loaded ruleset as a feedback profile
to tailor the optimization of the
If
.Cm profile
is specified, the currently loaded ruleset will be examined as a feedback
profile to tailor the optimization of the
.Ar quick
rules to the actual network behavior.
.Pp
@ -286,6 +340,14 @@ the ruleset optimizer should not be used or a
.Ar label
field should be added to all of the accounting rules to act as optimization
barriers.
.Pp
To retain compatibility with previous behaviour, a single
.Fl o
without any options will enable
.Cm basic
optimizations, and a second
.Fl o
will enable profiling.
.It Fl p Ar device
Use the device file
.Ar device
@ -350,7 +412,8 @@ When used together with
.Fl v ,
source tracking statistics are also shown.
.It Fl s Cm labels
Show per-rule statistics (label, evaluations, packets, bytes) of
Show per-rule statistics (label, evaluations, packets total, bytes total,
packets in, bytes in, packets out, bytes out) of
filter rules with labels, useful for accounting.
.It Fl s Cm timeouts
Show the current global timeouts.
@ -362,8 +425,11 @@ Show the list of tables.
Show the list of operating system fingerprints.
.It Fl s Cm Interfaces
Show the list of interfaces and interface drivers available to PF.
When used together with a double
When used together with
.Fl v ,
it additionally lists which interfaces have skip rules activated.
When used together with
.Fl vv ,
interface statistics are also shown.
.Fl i
can be used to select an interface or a group of interfaces.
@ -387,6 +453,13 @@ Add one or more addresses in a table.
Automatically create a nonexisting table.
.It Fl T Cm delete
Delete one or more addresses from a table.
.It Fl T Cm expire Ar number
Delete addresses which had their statistics cleared more than
.Ar number
seconds ago.
For entries which have never had their statistics cleared,
.Ar number
refers to the time they were added to the table.
.It Fl T Cm replace
Replace the addresses of the table.
Automatically create a nonexisting table.
@ -463,7 +536,7 @@ 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-
pass out to <test>\en" | pfctl -f-
# ping -qc10 ftp.openbsd.org
.Ed
.Pp

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl.h,v 1.37 2005/01/05 18:23:10 mcbride Exp $ */
/* $OpenBSD: pfctl.h,v 1.40 2007/02/09 11:25:27 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@ -33,6 +33,8 @@
#ifndef _PFCTL_H_
#define _PFCTL_H_
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
PFRB_IFACES, PFRB_TRANS, PFRB_MAX };
struct pfr_buffer {
@ -73,7 +75,7 @@ int pfr_buf_grow(struct pfr_buffer *, int);
int pfr_buf_load(struct pfr_buffer *, char *, int,
int (*)(struct pfr_buffer *, char *, int));
char *pfr_strerror(int);
int pfi_get_ifaces(const char *, struct pfi_if *, int *, int);
int pfi_get_ifaces(const char *, struct pfi_kif *, int *);
int pfi_clr_istats(const char *, int *, int);
void pfctl_print_title(char *);
@ -106,7 +108,6 @@ extern int loadopt;
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);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_altq.c,v 1.86 2005/02/28 14:04:51 henning Exp $ */
/* $OpenBSD: pfctl_altq.c,v 1.91 2006/11/28 00:08:50 henning Exp $ */
/*
* Copyright (c) 2002
@ -93,21 +93,6 @@ pfaltq_store(struct pf_altq *a)
TAILQ_INSERT_TAIL(&altqs, altq, entries);
}
void
pfaltq_free(struct pf_altq *a)
{
struct pf_altq *altq;
TAILQ_FOREACH(altq, &altqs, entries) {
if (strncmp(a->ifname, altq->ifname, IFNAMSIZ) == 0 &&
strncmp(a->qname, altq->qname, PF_QNAME_SIZE) == 0) {
TAILQ_REMOVE(&altqs, altq, entries);
free(altq);
return;
}
}
}
struct pf_altq *
pfaltq_lookup(const char *ifname)
{
@ -157,7 +142,7 @@ print_altq(const struct pf_altq *a, unsigned level, struct node_queue_bw *bw,
struct node_queue_opt *qopts)
{
if (a->qname[0] != 0) {
print_queue(a, level, bw, 0, qopts);
print_queue(a, level, bw, 1, qopts);
return;
}
@ -238,8 +223,8 @@ eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
pa->ifbandwidth = bw->bw_absolute;
else
if ((rate = getifspeed(pa->ifname)) == 0) {
fprintf(stderr, "cannot determine interface bandwidth "
"for %s, specify an absolute bandwidth\n",
fprintf(stderr, "interface %s does not know its bandwidth, "
"please specify an absolute bandwidth\n",
pa->ifname);
errors++;
} else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0)
@ -490,10 +475,7 @@ cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)
maxidle = ptime * maxidle;
else
maxidle = ptime * maxidle_s;
if (minburst)
offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
else
offtime = cptime;
offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
minidle = -((double)opts->maxpktsize * (double)nsPerByte);
/* scale parameters */
@ -698,8 +680,8 @@ eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)
}
if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
(opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
(opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0)) {
(opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) ||
(opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) {
warnx("m1 must be zero for convex curve: %s", pa->qname);
return (-1);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_optimize.c,v 1.5 2005/01/03 15:18:10 frantzen Exp $ */
/* $OpenBSD: pfctl_optimize.c,v 1.13 2006/10/31 14:17:45 mcbride Exp $ */
/*
* Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org>
@ -109,6 +109,10 @@ struct pf_rule_field {
PF_RULE_FIELD(prob, BARRIER),
PF_RULE_FIELD(max_states, BARRIER),
PF_RULE_FIELD(max_src_nodes, BARRIER),
PF_RULE_FIELD(max_src_states, BARRIER),
PF_RULE_FIELD(max_src_conn, BARRIER),
PF_RULE_FIELD(max_src_conn_rate, BARRIER),
PF_RULE_FIELD(anchor, BARRIER), /* for now */
/*
* These fields must be the same between all rules in the same superblock.
@ -120,10 +124,18 @@ struct pf_rule_field {
PF_RULE_FIELD(tagname, BREAK),
PF_RULE_FIELD(keep_state, BREAK),
PF_RULE_FIELD(qname, BREAK),
PF_RULE_FIELD(pqname, BREAK),
PF_RULE_FIELD(rt, BREAK),
PF_RULE_FIELD(allow_opts, BREAK),
PF_RULE_FIELD(rule_flag, BREAK),
PF_RULE_FIELD(action, BREAK),
PF_RULE_FIELD(log, BREAK),
PF_RULE_FIELD(quick, BREAK),
PF_RULE_FIELD(return_ttl, BREAK),
PF_RULE_FIELD(overload_tblname, BREAK),
PF_RULE_FIELD(flush, BREAK),
PF_RULE_FIELD(rpool, BREAK),
PF_RULE_FIELD(logif, BREAK),
/*
* Any fields not listed in this structure act as BREAK fields
@ -137,7 +149,7 @@ struct pf_rule_field {
*/
PF_RULE_FIELD(af, NOMERGE),
PF_RULE_FIELD(ifnot, NOMERGE),
PF_RULE_FIELD(ifname, NOMERGE),
PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */
PF_RULE_FIELD(match_tag_not, NOMERGE),
PF_RULE_FIELD(match_tagname, NOMERGE),
PF_RULE_FIELD(os_fingerprint, NOMERGE),
@ -170,7 +182,6 @@ struct pf_rule_field {
PF_RULE_FIELD(packets, DC),
PF_RULE_FIELD(bytes, DC),
PF_RULE_FIELD(kif, DC),
PF_RULE_FIELD(anchor, DC),
PF_RULE_FIELD(states, DC),
PF_RULE_FIELD(src_nodes, DC),
PF_RULE_FIELD(nr, DC),
@ -179,6 +190,9 @@ struct pf_rule_field {
PF_RULE_FIELD(pqid, DC),
PF_RULE_FIELD(anchor_relative, DC),
PF_RULE_FIELD(anchor_wildcard, DC),
PF_RULE_FIELD(tag, DC),
PF_RULE_FIELD(match_tag, DC),
PF_RULE_FIELD(overload_tbl, DC),
/* These fields should never be set in a PASS/BLOCK rule */
PF_RULE_FIELD(natpass, NEVER),
@ -198,6 +212,7 @@ void comparable_rule(struct pf_rule *, const struct pf_rule *, int);
int construct_superblocks(struct pfctl *, struct pf_opt_queue *,
struct superblocks *);
void exclude_supersets(struct pf_rule *, struct pf_rule *);
int interface_group(const char *);
int load_feedback_profile(struct pfctl *, struct superblocks *);
int optimize_superblock(struct pfctl *, struct superblock *);
int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
@ -240,25 +255,52 @@ int table_identifier;
int
pfctl_optimize_rules(struct pfctl *pf)
pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs)
{
struct superblocks superblocks;
struct pf_opt_queue opt_queue;
struct superblock *block;
struct pf_opt_rule *por;
int nr;
struct pf_rule *r;
struct pf_rulequeue *old_rules;
DEBUG("optimizing ruleset");
memset(&table_buffer, 0, sizeof(table_buffer));
skip_init();
TAILQ_INIT(&opt_queue);
if (TAILQ_FIRST(&pf->opt_queue))
nr = TAILQ_FIRST(&pf->opt_queue)->por_rule.nr;
old_rules = rs->rules[PF_RULESET_FILTER].active.ptr;
rs->rules[PF_RULESET_FILTER].active.ptr =
rs->rules[PF_RULESET_FILTER].inactive.ptr;
rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules;
/*
* XXX expanding the pf_opt_rule format throughout pfctl might allow
* us to avoid all this copying.
*/
while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr))
!= NULL) {
TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r,
entries);
if ((por = calloc(1, sizeof(*por))) == NULL)
err(1, "calloc");
memcpy(&por->por_rule, r, sizeof(*r));
if (TAILQ_FIRST(&r->rpool.list) != NULL) {
TAILQ_INIT(&por->por_rule.rpool.list);
pfctl_move_pool(&r->rpool, &por->por_rule.rpool);
} else
bzero(&por->por_rule.rpool,
sizeof(por->por_rule.rpool));
TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
}
TAILQ_INIT(&superblocks);
if (construct_superblocks(pf, &pf->opt_queue, &superblocks))
if (construct_superblocks(pf, &opt_queue, &superblocks))
goto error;
if (pf->opts & PF_OPT_OPTIMIZE_PROFILE) {
if (pf->optimize & PF_OPTIMIZE_PROFILE) {
if (load_feedback_profile(pf, &superblocks))
goto error;
}
@ -268,24 +310,21 @@ pfctl_optimize_rules(struct pfctl *pf)
goto error;
}
/*
* Optimizations are done so we turn off the optimization flag and
* put the rules right back into the regular codepath.
*/
pf->opts &= ~PF_OPT_OPTIMIZE;
rs->anchor->refcnt = 0;
while ((block = TAILQ_FIRST(&superblocks))) {
TAILQ_REMOVE(&superblocks, block, sb_entry);
while ((por = TAILQ_FIRST(&block->sb_rules))) {
TAILQ_REMOVE(&block->sb_rules, por, por_entry);
por->por_rule.nr = nr++;
if (pfctl_add_rule(pf, &por->por_rule,
por->por_anchor)) {
free(por);
goto error;
}
por->por_rule.nr = rs->anchor->refcnt++;
if ((r = calloc(1, sizeof(*r))) == NULL)
err(1, "calloc");
memcpy(r, &por->por_rule, sizeof(*r));
TAILQ_INIT(&r->rpool.list);
pfctl_move_pool(&por->por_rule.rpool, &r->rpool);
TAILQ_INSERT_TAIL(
rs->rules[PF_RULESET_FILTER].active.ptr,
r, entries);
free(por);
}
free(block);
@ -294,8 +333,8 @@ pfctl_optimize_rules(struct pfctl *pf)
return (0);
error:
while ((por = TAILQ_FIRST(&pf->opt_queue))) {
TAILQ_REMOVE(&pf->opt_queue, por, por_entry);
while ((por = TAILQ_FIRST(&opt_queue))) {
TAILQ_REMOVE(&opt_queue, por, por_entry);
if (por->por_src_tbl) {
pfr_buf_clear(por->por_src_tbl->pt_buf);
free(por->por_src_tbl->pt_buf);
@ -364,7 +403,8 @@ optimize_superblock(struct pfctl *pf, struct superblock *block)
printf("--- Superblock ---\n");
TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
printf(" ");
print_rule(&por->por_rule, por->por_anchor, 1);
print_rule(&por->por_rule, por->por_rule.anchor ?
por->por_rule.anchor->name : "", 1);
}
#endif /* OPT_DEBUG */
@ -373,7 +413,7 @@ optimize_superblock(struct pfctl *pf, struct superblock *block)
return (1);
if (combine_rules(pf, block))
return (1);
if ((pf->opts & PF_OPT_OPTIMIZE_PROFILE) &&
if ((pf->optimize & PF_OPTIMIZE_PROFILE) &&
TAILQ_FIRST(&block->sb_rules)->por_rule.quick &&
block->sb_profiled_block) {
if (block_feedback(pf, block))
@ -780,14 +820,16 @@ block_feedback(struct pfctl *pf, struct superblock *block)
*/
TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) {
comparable_rule(&a, &por1->por_rule, DC);
total_count += por1->por_rule.packets;
total_count += por1->por_rule.packets[0] +
por1->por_rule.packets[1];
TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
if (por2->por_profile_count)
continue;
comparable_rule(&b, &por2->por_rule, DC);
if (memcmp(&a, &b, sizeof(a)) == 0) {
por2->por_profile_count =
por1->por_rule.packets;
por1->por_rule.packets[0] +
por1->por_rule.packets[1];
break;
}
}
@ -851,6 +893,7 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
DEBUG("Loading %d active rules for a feedback profile", mnr);
for (nr = 0; nr < mnr; ++nr) {
struct pf_ruleset *rs;
if ((por = calloc(1, sizeof(*por))) == NULL) {
warn("calloc");
return (1);
@ -861,8 +904,8 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
return (1);
}
memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
strlcpy(por->por_anchor, pr.anchor_call,
sizeof(por->por_anchor));
rs = pf_find_or_create_ruleset(pr.anchor_call);
por->por_rule.anchor = rs->anchor;
if (TAILQ_EMPTY(&por->por_rule.rpool.list))
memset(&por->por_rule.rpool, 0,
sizeof(por->por_rule.rpool));
@ -1045,6 +1088,7 @@ skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b)
return (1);
return (0);
case PF_ADDR_NOROUTE:
case PF_ADDR_URPFFAILED:
return (0);
case PF_ADDR_TABLE:
return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname));
@ -1116,6 +1160,7 @@ skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b)
return (1);
return (0);
case PF_ADDR_NOROUTE:
case PF_ADDR_URPFFAILED:
return (0);
case PF_ADDR_TABLE:
return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname));
@ -1267,8 +1312,8 @@ pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
tablenum++;
if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, pf->anchor,
tbl->pt_buf, pf->tticket)) {
if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
pf->anchor->name, tbl->pt_buf, pf->anchor->ruleset.tticket)) {
warn("failed to create table %s", tbl->pt_name);
return (1);
}
@ -1367,15 +1412,34 @@ superblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
}
}
/* 'anchor' heads and per-rule src-track are also hard breaks */
if (por->por_anchor[0] != '\0' ||
(por->por_rule.rule_flag & PFRULE_RULESRCTRACK))
/* per-rule src-track is also a hard break */
if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK)
return (0);
/*
* Have to handle interface groups seperately. Consider the following
* rules:
* block on EXTIFS to any port 22
* pass on em0 to any port 22
* (where EXTIFS is an arbitrary interface group)
* The optimizer may decide to re-order the pass rule in front of the
* block rule. But what if EXTIFS includes em0??? Such a reordering
* would change the meaning of the ruleset.
* We can't just lookup the EXTIFS group and check if em0 is a member
* because the user is allowed to add interfaces to a group during
* runtime.
* Ergo interface groups become a defacto superblock break :-(
*/
if (interface_group(por->por_rule.ifname) ||
interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) {
if (strcasecmp(por->por_rule.ifname,
TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0)
return (0);
}
comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE);
comparable_rule(&b, &por->por_rule, NOMERGE);
if (strcmp(TAILQ_FIRST(&block->sb_rules)->por_anchor,
por->por_anchor) == 0 && memcmp(&a, &b, sizeof(a)) == 0)
if (memcmp(&a, &b, sizeof(a)) == 0)
return (1);
#ifdef OPT_DEBUG
@ -1418,6 +1482,24 @@ superblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
}
/*
* Figure out if an interface name is an actual interface or actually a
* group of interfaces.
*/
int
interface_group(const char *ifname)
{
if (ifname == NULL || !ifname[0])
return (0);
/* Real interfaces must end in a number, interface groups do not */
if (isdigit(ifname[strlen(ifname) - 1]))
return (0);
else
return (1);
}
/*
* Make a rule that can directly compared by memcmp()
*/

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_osfp.c,v 1.12 2005/02/17 13:18:00 aaron Exp $ */
/* $OpenBSD: pfctl_osfp.c,v 1.15 2006/12/13 05:10:15 itojun Exp $ */
/*
* Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org>
@ -23,6 +23,10 @@
#include <net/if.h>
#include <net/pfvar.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@ -240,6 +244,10 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
sizeof(fp.fp_os.fp_subtype_nm));
add_fingerprint(dev, opts, &fp);
fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6);
fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip);
add_fingerprint(dev, opts, &fp);
}
if (class)
@ -250,6 +258,8 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
free(subtype);
if (desc)
free(desc);
if (tcpopts)
free(tcpopts);
fclose(in);
@ -762,7 +772,6 @@ sort_name_list(int opts, struct name_list *nml)
LIST_INSERT_AFTER(nmlast, nm, nm_entry);
nmlast = nm;
}
return;
}
/* parse the next integer in a formatted config file line */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_parser.c,v 1.211 2004/12/07 10:33:41 dhartmei Exp $ */
/* $OpenBSD: pfctl_parser.c,v 1.234 2006/10/31 23:46:24 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@ -54,6 +54,7 @@
#include <errno.h>
#include <err.h>
#include <ifaddrs.h>
#include <unistd.h>
#include "pfctl_parser.h"
#include "pfctl.h"
@ -66,6 +67,7 @@ void print_fromto(struct pf_rule_addr *, pf_osfp_t,
struct pf_rule_addr *, u_int8_t, u_int8_t, int);
int ifa_skip_if(const char *filter, struct node_host *p);
struct node_host *ifa_grouplookup(const char *, int);
struct node_host *host_if(const char *, int);
struct node_host *host_v4(const char *, int);
struct node_host *host_v6(const char *, int);
@ -479,9 +481,11 @@ const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
void
print_status(struct pf_status *s, int opts)
{
char statline[80], *running;
time_t runtime;
int i;
char statline[80], *running;
time_t runtime;
int i;
char buf[PF_MD5_DIGEST_LENGTH * 2 + 1];
static const char hex[] = "0123456789abcdef";
runtime = time(NULL) - s->since;
running = s->running ? "Enabled" : "Disabled";
@ -515,7 +519,18 @@ print_status(struct pf_status *s, int opts)
printf("%15s\n\n", "Debug: Loud");
break;
}
printf("Hostid: 0x%08x\n\n", ntohl(s->hostid));
if (opts & PF_OPT_VERBOSE) {
printf("Hostid: 0x%08x\n", ntohl(s->hostid));
for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) {
buf[i + i] = hex[s->pf_chksum[i] >> 4];
buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f];
}
buf[i + i] = '\0';
printf("Checksum: 0x%s\n\n", buf);
}
if (s->ifname[0] != 0) {
printf("Interface Stats for %-16s %5s %16s\n",
s->ifname, "IPv4", "IPv6");
@ -623,7 +638,9 @@ print_src_node(struct pf_src_node *sn, int opts)
printf(", expires in %.2u:%.2u:%.2u",
sn->expire, min, sec);
}
printf(", %u pkts, %u bytes", sn->packets, sn->bytes);
printf(", %llu pkts, %llu bytes",
sn->packets[0] + sn->packets[1],
sn->bytes[0] + sn->bytes[1]);
switch (sn->ruletype) {
case PF_NAT:
if (sn->rule.nr != -1)
@ -656,10 +673,13 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
printf("@%d ", r->nr);
if (r->action > PF_NORDR)
printf("action(%d)", r->action);
else if (anchor_call[0])
printf("%s \"%s\"", anchortypes[r->action],
anchor_call);
else {
else if (anchor_call[0]) {
if (anchor_call[0] == '_') {
printf("%s", anchortypes[r->action]);
} else
printf("%s \"%s\"", anchortypes[r->action],
anchor_call);
} else {
printf("%s", actiontypes[r->action]);
if (r->natpass)
printf(" pass");
@ -714,10 +734,22 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
printf(" in");
else if (r->direction == PF_OUT)
printf(" out");
if (r->log == 1)
if (r->log) {
printf(" log");
else if (r->log == 2)
printf(" log-all");
if (r->log & ~PF_LOG || r->logif) {
int count = 0;
printf(" (");
if (r->log & PF_LOG_ALL)
printf("%sall", count++ ? ", " : "");
if (r->log & PF_LOG_SOCKET_LOOKUP)
printf("%suser", count++ ? ", " : "");
if (r->logif)
printf("%sto pflog%u", count++ ? ", " : "",
r->logif);
printf(")");
}
}
if (r->quick)
printf(" quick");
if (r->ifname[0]) {
@ -767,7 +799,11 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
print_flags(r->flags);
printf("/");
print_flags(r->flagset);
}
} else if (r->action == PF_PASS &&
(!r->proto || r->proto == IPPROTO_TCP) &&
!(r->rule_flag & PFRULE_FRAGMENT) &&
!anchor_call[0] && r->keep_state)
printf(" flags any");
if (r->type) {
const struct icmptypeent *it;
@ -792,7 +828,9 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
}
if (r->tos)
printf(" tos 0x%2.2x", r->tos);
if (r->keep_state == PF_STATE_NORMAL)
if (!r->keep_state && r->action == PF_PASS && !anchor_call[0])
printf(" no state");
else if (r->keep_state == PF_STATE_NORMAL)
printf(" keep state");
else if (r->keep_state == PF_STATE_MODULATE)
printf(" modulate state");
@ -820,7 +858,7 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
opts = 1;
if (r->rule_flag & PFRULE_SRCTRACK)
opts = 1;
if (r->rule_flag & (PFRULE_IFBOUND | PFRULE_GRBOUND))
if (r->rule_flag & PFRULE_IFBOUND)
opts = 1;
for (i = 0; !opts && i < PFTM_MAX; ++i)
if (r->timeout[i])
@ -888,12 +926,6 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
printf("if-bound");
opts = 0;
}
if (r->rule_flag & PFRULE_GRBOUND) {
if (!opts)
printf(", ");
printf("group-bound");
opts = 0;
}
for (i = 0; i < PFTM_MAX; ++i)
if (r->timeout[i]) {
int j;
@ -901,12 +933,13 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
if (!opts)
printf(", ");
opts = 0;
for (j = 0; j < sizeof(pf_timeouts) /
sizeof(pf_timeouts[0]); ++j)
for (j = 0; pf_timeouts[j].name != NULL;
++j)
if (pf_timeouts[j].timeout == i)
break;
printf("%s %u", j == PFTM_MAX ? "inv.timeout" :
pf_timeouts[j].name, r->timeout[i]);
printf("%s %u", pf_timeouts[j].name == NULL ?
"inv.timeout" : pf_timeouts[j].name,
r->timeout[i]);
}
printf(")");
}
@ -946,13 +979,14 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
printf(" !");
printf(" tagged %s", r->match_tagname);
}
if (r->rtableid != -1)
printf(" rtable %u", r->rtableid);
if (!anchor_call[0] && (r->action == PF_NAT ||
r->action == PF_BINAT || r->action == PF_RDR)) {
printf(" -> ");
print_pool(&r->rpool, r->rpool.proxy_port[0],
r->rpool.proxy_port[1], r->af, r->action);
}
printf("\n");
}
void
@ -1145,13 +1179,31 @@ ifa_load(void)
}
struct node_host *
ifa_exists(const char *ifa_name, int group_ok)
ifa_exists(const char *ifa_name)
{
struct node_host *n;
struct ifgroupreq ifgr;
int s;
if (iftab == NULL)
ifa_load();
/* check wether this is a group */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
err(1, "socket");
bzero(&ifgr, sizeof(ifgr));
strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
/* fake a node_host */
if ((n = calloc(1, sizeof(*n))) == NULL)
err(1, "calloc");
if ((n->ifname = strdup(ifa_name)) == NULL)
err(1, "strdup");
close(s);
return (n);
}
close(s);
for (n = iftab; n; n = n->next) {
if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ))
return (n);
@ -1160,6 +1212,47 @@ ifa_exists(const char *ifa_name, int group_ok)
return (NULL);
}
struct node_host *
ifa_grouplookup(const char *ifa_name, int flags)
{
struct ifg_req *ifg;
struct ifgroupreq ifgr;
int s, len;
struct node_host *n, *h = NULL;
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
err(1, "socket");
bzero(&ifgr, sizeof(ifgr));
strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
close(s);
return (NULL);
}
len = ifgr.ifgr_len;
if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
err(1, "calloc");
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
err(1, "SIOCGIFGMEMB");
for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
ifg++) {
len -= sizeof(struct ifg_req);
if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL)
continue;
if (h == NULL)
h = n;
else {
h->tail->next = n;
h->tail = n->tail;
}
}
free(ifgr.ifgr_groups);
close(s);
return (h);
}
struct node_host *
ifa_lookup(const char *ifa_name, int flags)
{
@ -1167,6 +1260,9 @@ ifa_lookup(const char *ifa_name, int flags)
int got4 = 0, got6 = 0;
const char *last_if = NULL;
if ((h = ifa_grouplookup(ifa_name, flags)) != NULL)
return (h);
if (!strncmp(ifa_name, "self", IFNAMSIZ))
ifa_name = NULL;
@ -1344,7 +1440,7 @@ host_if(const char *s, int mask)
free(ps);
return (NULL);
}
if (ifa_exists(ps, 1) || !strncmp(ps, "self", IFNAMSIZ)) {
if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) {
/* interface with this name exists */
h = ifa_lookup(ps, flags);
for (n = h; n != NULL && mask > -1; n = n->next)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_parser.h,v 1.80 2005/02/07 18:18:14 david Exp $ */
/* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@ -47,14 +47,17 @@
#define PF_OPT_DEBUG 0x0200
#define PF_OPT_SHOWALL 0x0400
#define PF_OPT_OPTIMIZE 0x0800
#define PF_OPT_OPTIMIZE_PROFILE 0x1000
#define PF_OPT_MERGE 0x2000
#define PF_OPT_RECURSE 0x4000
#define PF_TH_ALL 0xFF
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
#define PF_OPTIMIZE_BASIC 0x0001
#define PF_OPTIMIZE_PROFILE 0x0002
#define FCNT_NAMES { \
"searches", \
"inserts", \
@ -63,24 +66,25 @@
}
struct pfr_buffer; /* forward definition */
struct pf_opt_rule;
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
struct pfctl {
int dev;
int opts;
int optimize;
int loadopt;
u_int32_t tticket; /* table ticket */
int asd; /* anchor stack depth */
int bn; /* brace number */
int brace;
int tdirty; /* kernel dirty */
u_int32_t rule_nr;
#define PFCTL_ANCHOR_STACK_DEPTH 64
struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH];
struct pfioc_pooladdr paddr;
struct pfioc_altq *paltq;
struct pfioc_queue *pqueue;
struct pfr_buffer *trans;
const char *anchor;
struct pf_anchor *anchor, *alast;
const char *ruleset;
struct pf_opt_queue opt_queue;
/* 'set foo' options */
u_int32_t timeout[PFTM_MAX];
@ -117,10 +121,6 @@ struct node_host {
struct node_host *next;
struct node_host *tail;
};
/* special flags used by ifa_exists */
#define PF_IFA_FLAG_GROUP 0x10000
#define PF_IFA_FLAG_DYNAMIC 0x20000
#define PF_IFA_FLAG_CLONABLE 0x40000
struct node_os {
char *os;
@ -180,19 +180,20 @@ struct pf_opt_rule {
struct pf_rule por_rule;
struct pf_opt_tbl *por_src_tbl;
struct pf_opt_tbl *por_dst_tbl;
char por_anchor[MAXPATHLEN];
u_int64_t por_profile_count;
TAILQ_ENTRY(pf_opt_rule) por_entry;
TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT];
};
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
int pfctl_rules(int, char *, int, char *, struct pfr_buffer *);
int pfctl_optimize_rules(struct pfctl *);
int pfctl_rules(int, char *, FILE *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *);
int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t);
void pfctl_move_pool(struct pf_pool *, struct pf_pool *);
void pfctl_clear_pool(struct pf_pool *);
int pfctl_set_timeout(struct pfctl *, const char *, int, int);
@ -205,7 +206,7 @@ int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
int parse_rules(FILE *, struct pfctl *);
int parse_flags(char *);
int pfctl_load_anchors(int, int, struct pfr_buffer *);
int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
void print_src_node(struct pf_src_node *, int);
@ -267,7 +268,7 @@ void set_ipmask(struct node_host *, u_int8_t);
int check_netmask(struct node_host *, sa_family_t);
int unmask(struct pf_addr *, sa_family_t);
void ifa_load(void);
struct node_host *ifa_exists(const char *, int);
struct node_host *ifa_exists(const char *);
struct node_host *ifa_lookup(const char *, int);
struct node_host *host(const char *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_radix.c,v 1.26 2004/06/14 20:44:22 cedric Exp $ */
/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
@ -421,7 +421,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
/* interface management code */
int
pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
{
struct pfioc_iface io;
@ -430,7 +430,6 @@ pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
return (-1);
}
bzero(&io, sizeof io);
io.pfiio_flags = flags;
if (filter != NULL)
if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
sizeof(io.pfiio_name)) {
@ -451,7 +450,7 @@ pfi_get_ifaces(const char *filter, struct pfi_if *buf, int *size, int flags)
size_t buf_esize[PFRB_MAX] = { 0,
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
sizeof(struct pfi_if), sizeof(struct pfioc_trans_e)
sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
};
/*

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pfctl_table.c,v 1.62 2004/12/22 17:17:55 dhartmei Exp $ */
/* $OpenBSD: pfctl_table.c,v 1.66 2007/03/01 17:20:54 deraadt Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
@ -61,8 +61,7 @@ static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
static void print_astats(struct pfr_astats *, int);
static void radix_perror(void);
static void xprintf(int, const char *, ...);
static void print_iface(struct pfi_if *, int);
static void oprintf(int, int, const char *, int *, int);
static void print_iface(struct pfi_kif *, int);
static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
{ "In/Block:", "In/Pass:", "In/XPass:" },
@ -175,7 +174,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
break;
}
if (opts & PF_OPT_SHOWALL && b.pfrb_size > 0)
if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
pfctl_print_title("TABLES:");
PFRB_FOREACH(p, &b)
@ -254,6 +253,42 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "expire")) {
const char *errstr;
u_int lifetime;
b.pfrb_type = PFRB_ASTATS;
b2.pfrb_type = PFRB_ADDRS;
if (argc != 1 || file != NULL)
usage();
lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
if (errstr)
errx(1, "expiry time: %s", errstr);
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(p, &b)
if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
lifetime)
if (pfr_buf_add(&b2,
&((struct pfr_astats *)p)->pfras_a))
err(1, "duplicate buffer");
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
&ndel, flags));
xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b2)
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "show")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
PFRB_ASTATS : PFRB_ADDRS;
@ -291,7 +326,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
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))
if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
PFRB_FOREACH(a, &b)
if (a->pfra_fback == PFR_FB_MATCH)
print_addrx(a, NULL,
@ -539,17 +574,15 @@ int
pfctl_show_ifaces(const char *filter, int opts)
{
struct pfr_buffer b;
struct pfi_if *p;
int i = 0, f = PFI_FLAG_GROUP|PFI_FLAG_INSTANCE;
struct pfi_kif *p;
int i = 0;
if (filter != NULL && *filter && !isdigit(filter[strlen(filter)-1]))
f &= ~PFI_FLAG_INSTANCE;
bzero(&b, sizeof(b));
b.pfrb_type = PFRB_IFACES;
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size, f)) {
if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) {
radix_perror();
return (1);
}
@ -565,46 +598,30 @@ pfctl_show_ifaces(const char *filter, int opts)
}
void
print_iface(struct pfi_if *p, int opts)
print_iface(struct pfi_kif *p, int opts)
{
time_t tzero = p->pfif_tzero;
int flags = (opts & PF_OPT_VERBOSE) ? p->pfif_flags : 0;
int first = 1;
time_t tzero = p->pfik_tzero;
int i, af, dir, act;
printf("%s", p->pfif_name);
oprintf(flags, PFI_IFLAG_INSTANCE, "instance", &first, 0);
oprintf(flags, PFI_IFLAG_GROUP, "group", &first, 0);
oprintf(flags, PFI_IFLAG_CLONABLE, "clonable", &first, 0);
oprintf(flags, PFI_IFLAG_DYNAMIC, "dynamic", &first, 0);
oprintf(flags, PFI_IFLAG_ATTACHED, "attached", &first, 0);
oprintf(flags, PFI_IFLAG_SKIP, "skipped", &first, 1);
printf("%s", p->pfik_name);
if (opts & PF_OPT_VERBOSE) {
if (p->pfik_flags & PFI_IFLAG_SKIP)
printf(" (skip)");
}
printf("\n");
if (!(opts & PF_OPT_VERBOSE2))
return;
printf("\tCleared: %s", ctime(&tzero));
printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
p->pfif_states, p->pfif_rules);
p->pfik_states, p->pfik_rules);
for (i = 0; i < 8; i++) {
af = (i>>2) & 1;
dir = (i>>1) &1;
act = i & 1;
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
istats_text[af][dir][act],
(unsigned long long)p->pfif_packets[af][dir][act],
(unsigned long long)p->pfif_bytes[af][dir][act]);
(unsigned long long)p->pfik_packets[af][dir][act],
(unsigned long long)p->pfik_bytes[af][dir][act]);
}
}
void
oprintf(int flags, int flag, const char *s, int *first, int last)
{
if (flags & flag) {
printf(*first ? "\t(%s" : ", %s", s);
*first = 0;
}
if (last && !*first)
printf(")");
}

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: pflogd.8,v 1.25 2005/01/02 18:15:02 jmc Exp $
.\" $OpenBSD: pflogd.8,v 1.32 2006/12/08 10:26:38 joel Exp $
.\"
.\" Copyright (c) 2001 Can Erkin Acar. All rights reserved.
.\"
@ -35,14 +35,17 @@
.Op Fl Dx
.Op Fl d Ar delay
.Op Fl f Ar filename
.Op Fl i Ar interface
.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
to a
.Xr pflog 4
interface, normally
.Pa pflog0 ,
and writes the packets to a logfile (normally
.Pa /var/log/pflog )
in
@ -81,7 +84,9 @@ temporarily uses the old snaplen to keep the log file consistent.
tries to preserve the integrity of the log file against I/O errors.
Furthermore, integrity of an existing log file is verified before
appending.
If there is an invalid log file or an I/O error, logging is suspended until a
If there is an invalid log file or an I/O error, the log file is moved
out of the way and a new one is created.
If a new file cannot be created, logging is suspended until a
.Dv SIGHUP
or a
.Dv SIGALRM
@ -101,11 +106,19 @@ If not specified, the default is 60 seconds.
Log output filename.
Default is
.Pa /var/log/pflog .
.It Fl i Ar interface
Specifies the
.Xr pflog 4
interface to use.
By default,
.Nm
will use
.Ar pflog0 .
.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
bytes of data from each packet rather than the default of 116.
The default of 116 is adequate for IP, ICMP, TCP, and UDP headers but may
truncate protocol information for other protocols.
Other file parsers may desire a higher snaplen.
.It Fl x
@ -129,6 +142,13 @@ Log specific tcp packets to a different log file with a large snaplen
# pflogd -s 1600 -f suspicious.log port 80 and host evilhost
.Ed
.Pp
Log from another
.Xr pflog 4
interface, excluding specific packets:
.Bd -literal -offset indent
# pflogd -i pflog3 -f network3.log "not (tcp and port 23)"
.Ed
.Pp
Display binary logs:
.Bd -literal -offset indent
# tcpdump -n -e -ttt -r /var/log/pflog
@ -148,7 +168,7 @@ 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
.Bl -tag -width "ruleset authpf " -compact
.It ip
Address family equals IPv4.
.It ip6
@ -157,12 +177,16 @@ Address family equals IPv6.
Interface name equals "kue0".
.It on kue0
Interface name equals "kue0".
.It ruleset authpf
Ruleset name equals "authpf".
.It rulenum 10
Rule number equals 10.
.It reason match
Reason equals match.
Also accepts "bad-offset", "fragment", "bad-timestamp", "short",
"normalize" and "memory".
"normalize", "memory", "congestion", "ip-option", "proto-cksum",
"state-mismatch", "state-insert", "state-limit", "src-limit",
and "synproxy".
.It action pass
Action equals pass.
Also accepts "block".
@ -190,4 +214,6 @@ The
command appeared in
.Ox 3.0 .
.Sh AUTHORS
Can Erkin Acar
.Nm
was written by
.An Can Erkin Acar Aq canacar@openbsd.org .

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pflogd.c,v 1.33 2005/02/09 12:09:30 henning Exp $ */
/* $OpenBSD: pflogd.c,v 1.37 2006/10/26 13:34:47 jmc Exp $ */
/*
* Copyright (c) 2001 Theo de Raadt
@ -73,7 +73,7 @@ int flush_buffer(FILE *);
int init_pcap(void);
void logmsg(int, const char *, ...);
void purge_buffer(void);
int reset_dump(void);
int reset_dump(int);
int scan_dump(FILE *, off_t);
int set_snaplen(int);
void set_suspended(int);
@ -82,6 +82,8 @@ void sig_close(int);
void sig_hup(int);
void usage(void);
static int try_reset_dump(int);
/* buffer must always be greater than snaplen */
static int bufpkt = 0; /* number of packets in buffer */
static int buflen = 0; /* allocated size of buffer */
@ -100,8 +102,9 @@ set_suspended(int s)
return;
suspended = s;
setproctitle("[%s] -s %d -f %s",
suspended ? "suspended" : "running", cur_snaplen, filename);
setproctitle("[%s] -s %d -i %s -f %s",
suspended ? "suspended" : "running",
cur_snaplen, interface, filename);
}
char *
@ -147,8 +150,9 @@ logmsg(int pri, const char *message, ...)
__dead void
usage(void)
{
fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename] ");
fprintf(stderr, "[-s snaplen] [expression]\n");
fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]");
fprintf(stderr, " [-i interface] [-s snaplen]\n");
fprintf(stderr, " [expression]\n");
exit(1);
}
@ -228,7 +232,25 @@ set_snaplen(int snap)
}
int
reset_dump(void)
reset_dump(int nomove)
{
int ret;
for (;;) {
ret = try_reset_dump(nomove);
if (ret <= 0)
break;
}
return (ret);
}
/*
* tries to (re)open log file, nomove flag is used with -x switch
* returns 0: success, 1: retry (log moved), -1: error
*/
int
try_reset_dump(int nomove)
{
struct pcap_file_header hdr;
struct stat st;
@ -250,26 +272,26 @@ reset_dump(void)
*/
fd = priv_open_log();
if (fd < 0)
return (1);
return (-1);
fp = fdopen(fd, "a+");
if (fp == NULL) {
close(fd);
logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
return (1);
close(fd);
return (-1);
}
if (fstat(fileno(fp), &st) == -1) {
fclose(fp);
logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
return (1);
fclose(fp);
return (-1);
}
/* set FILE unbuffered, we do our own buffering */
if (setvbuf(fp, NULL, _IONBF, 0)) {
fclose(fp);
logmsg(LOG_ERR, "Failed to set output buffers");
return (1);
fclose(fp);
return (-1);
}
#define TCPDUMP_MAGIC 0xa1b2c3d4
@ -277,11 +299,9 @@ reset_dump(void)
if (st.st_size == 0) {
if (snaplen != cur_snaplen) {
logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
if (set_snaplen(snaplen)) {
fclose(fp);
if (set_snaplen(snaplen))
logmsg(LOG_WARNING,
"Failed, using old settings");
}
}
hdr.magic = TCPDUMP_MAGIC;
hdr.version_major = PCAP_VERSION_MAJOR;
@ -293,11 +313,15 @@ reset_dump(void)
if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
fclose(fp);
return (1);
return (-1);
}
} else if (scan_dump(fp, st.st_size)) {
/* XXX move file and continue? */
fclose(fp);
if (nomove || priv_move_log()) {
logmsg(LOG_ERR,
"Invalid/incompatible log file, move it away");
return (-1);
}
return (1);
}
@ -336,7 +360,6 @@ scan_dump(FILE *fp, off_t size)
hdr.version_minor != PCAP_VERSION_MINOR ||
hdr.linktype != hpcap->linktype ||
hdr.snaplen > PFLOGD_MAXSNAPLEN) {
logmsg(LOG_ERR, "Invalid/incompatible log file, move it away");
return (1);
}
@ -511,7 +534,7 @@ main(int argc, char **argv)
closefrom(STDERR_FILENO + 1);
while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) {
while ((ch = getopt(argc, argv, "Dxd:f:i:s:")) != -1) {
switch (ch) {
case 'D':
Debug = 1;
@ -524,6 +547,9 @@ main(int argc, char **argv)
case 'f':
filename = optarg;
break;
case 'i':
interface = optarg;
break;
case 's':
snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
&errstr);
@ -596,7 +622,7 @@ main(int argc, char **argv)
bufpkt = 0;
}
if (reset_dump()) {
if (reset_dump(Xflag) < 0) {
if (Xflag)
return (1);
@ -614,7 +640,7 @@ main(int argc, char **argv)
if (gotsig_close)
break;
if (gotsig_hup) {
if (reset_dump()) {
if (reset_dump(0)) {
logmsg(LOG_ERR,
"Logging suspended: open error");
set_suspended(1);
@ -625,6 +651,8 @@ main(int argc, char **argv)
if (gotsig_alrm) {
if (dpcap)
flush_buffer(dpcap);
else
gotsig_hup = 1;
gotsig_alrm = 0;
alarm(delay);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pflogd.h,v 1.2 2004/01/15 20:15:14 canacar Exp $ */
/* $OpenBSD: pflogd.h,v 1.3 2006/01/15 16:38:04 canacar Exp $ */
/*
* Copyright (c) 2003 Can Erkin Acar
@ -37,6 +37,7 @@ void logmsg(int priority, const char *message, ...);
int priv_init(void);
int priv_set_snaplen(int snaplen);
int priv_open_log(void);
int priv_move_log(void);
pcap_t *pcap_open_live_fd(int fd, int snaplen, char *ebuf);
void set_pcap_filter(void);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: privsep.c,v 1.13 2004/12/22 09:21:02 otto Exp $ */
/* $OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $ */
/*
* Copyright (c) 2003 Can Erkin Acar
@ -16,7 +16,6 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
@ -28,6 +27,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pcap.h>
#include <pcap-int.h>
#include <pwd.h>
@ -41,6 +41,7 @@
enum cmd_types {
PRIV_SET_SNAPLEN, /* set the snaplength */
PRIV_MOVE_LOG, /* move logfile away */
PRIV_OPEN_LOG /* open logfile for appending */
};
@ -55,10 +56,8 @@ static int may_read(int, void *, size_t);
static void must_read(int, void *, size_t);
static void must_write(int, void *, size_t);
static int set_snaplen(int snap);
static int move_log(const char *name);
/* bpf filter expression common to parent and child */
extern char *filter;
extern char *errbuf;
extern char *filename;
extern pcap_t *hpcap;
@ -96,16 +95,12 @@ priv_init(void)
err(1, "unable to chdir");
gidset[0] = pw->pw_gid;
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
err(1, "setresgid() failed");
if (setgroups(1, gidset) == -1)
err(1, "setgroups() failed");
if (setegid(pw->pw_gid) == -1)
err(1, "setegid() failed");
if (setgid(pw->pw_gid) == -1)
err(1, "setgid() failed");
if (seteuid(pw->pw_uid) == -1)
err(1, "seteuid() failed");
if (setuid(pw->pw_uid) == -1)
err(1, "setuid() failed");
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
err(1, "setresuid() failed");
close(socks[0]);
priv_fd = socks[1];
return 0;
@ -159,6 +154,13 @@ priv_init(void)
close(fd);
break;
case PRIV_MOVE_LOG:
logmsg(LOG_DEBUG,
"[priv]: msg PRIV_MOVE_LOG received");
ret = move_log(filename);
must_write(socks[0], &ret, sizeof(int));
break;
default:
logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
_exit(1);
@ -182,6 +184,47 @@ set_snaplen(int snap)
return 0;
}
static int
move_log(const char *name)
{
char ren[PATH_MAX];
int len;
for (;;) {
int fd;
len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
name, arc4random());
if (len >= sizeof(ren)) {
logmsg(LOG_ERR, "[priv] new name too long");
return (1);
}
/* lock destinanion */
fd = open(ren, O_CREAT|O_EXCL, 0);
if (fd >= 0) {
close(fd);
break;
}
/* if file exists, try another name */
if (errno != EEXIST && errno != EINTR) {
logmsg(LOG_ERR, "[priv] failed to create new name: %s",
strerror(errno));
return (1);
}
}
if (rename(name, ren)) {
logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
name, ren, strerror(errno));
return (1);
}
logmsg(LOG_NOTICE,
"[priv]: log file %s moved to %s", name, ren);
return (0);
}
/*
* send the snaplength to privileged process
@ -223,6 +266,21 @@ priv_open_log(void)
return (fd);
}
/* Move-away and reopen log-file */
int
priv_move_log(void)
{
int cmd, ret;
if (priv_fd < 0)
errx(1, "%s: called from privileged portion\n", __func__);
cmd = PRIV_MOVE_LOG;
must_write(priv_fd, &cmd, sizeof(int));
must_read(priv_fd, &ret, sizeof(int));
return (ret);
}
/* If priv parent gets a TERM or HUP, pass it through to child instead */
static void

View File

@ -0,0 +1,397 @@
/* $OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
/*
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
*
* 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 <syslog.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "filter.h"
/* From netinet/in.h, but only _KERNEL_ gets them. */
#define satosin(sa) ((struct sockaddr_in *)(sa))
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *,
u_int16_t, u_int8_t);
int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
struct sockaddr_in *, u_int8_t);
int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
struct sockaddr_in6 *, u_int8_t);
static struct pfioc_pooladdr pfp;
static struct pfioc_rule pfr;
static struct pfioc_trans pft;
static struct pfioc_trans_e pfte[TRANS_SIZE];
static int dev, rule_log;
static char *qname;
int
add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
{
if (!src || !dst || !d_port || !proto) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1)
return (-1);
pfr.rule.direction = dir;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low,
u_int16_t nat_range_high, u_int8_t proto)
{
if (!src || !dst || !d_port || !nat || !nat_range_low || !proto ||
(src->sa_family != nat->sa_family)) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1)
return (-1);
if (nat->sa_family == AF_INET) {
memcpy(&pfp.addr.addr.v.a.addr.v4,
&satosin(nat)->sin_addr.s_addr, 4);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfp.addr.addr.v.a.addr.v6,
&satosin6(nat)->sin6_addr.s6_addr, 16);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
}
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
return (-1);
pfr.rule.rpool.proxy_port[0] = nat_range_low;
pfr.rule.rpool.proxy_port[1] = nat_range_high;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
{
if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
(src->sa_family != rdr->sa_family)) {
errno = EINVAL;
return (-1);
}
if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1)
return (-1);
if (rdr->sa_family == AF_INET) {
memcpy(&pfp.addr.addr.v.a.addr.v4,
&satosin(rdr)->sin_addr.s_addr, 4);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfp.addr.addr.v.a.addr.v6,
&satosin6(rdr)->sin6_addr.s6_addr, 16);
memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16);
}
if (ioctl(dev, DIOCADDADDR, &pfp) == -1)
return (-1);
pfr.rule.rpool.proxy_port[0] = rdr_port;
if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
return (-1);
return (0);
}
int
do_commit(void)
{
if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
return (-1);
return (0);
}
int
do_rollback(void)
{
if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
return (-1);
return (0);
}
void
init_filter(char *opt_qname, int opt_verbose)
{
struct pf_status status;
qname = opt_qname;
if (opt_verbose == 1)
rule_log = PF_LOG;
else if (opt_verbose == 2)
rule_log = PF_LOG_ALL;
dev = open("/dev/pf", O_RDWR);
if (dev == -1) {
syslog(LOG_ERR, "can't open /dev/pf");
exit(1);
}
if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
syslog(LOG_ERR, "DIOCGETSTATUS");
exit(1);
}
if (!status.running) {
syslog(LOG_ERR, "pf is disabled");
exit(1);
}
}
int
prepare_commit(u_int32_t id)
{
char an[PF_ANCHOR_NAME_SIZE];
int i;
memset(&pft, 0, sizeof pft);
pft.size = TRANS_SIZE;
pft.esize = sizeof pfte[0];
pft.array = pfte;
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
getpid(), id);
for (i = 0; i < TRANS_SIZE; i++) {
memset(&pfte[i], 0, sizeof pfte[0]);
strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE);
switch (i) {
case TRANS_FILTER:
pfte[i].rs_num = PF_RULESET_FILTER;
break;
case TRANS_NAT:
pfte[i].rs_num = PF_RULESET_NAT;
break;
case TRANS_RDR:
pfte[i].rs_num = PF_RULESET_RDR;
break;
default:
errno = EINVAL;
return (-1);
}
}
if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
return (-1);
return (0);
}
int
prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src,
struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
{
char an[PF_ANCHOR_NAME_SIZE];
if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
(src->sa_family != dst->sa_family)) {
errno = EPROTONOSUPPORT;
return (-1);
}
memset(&pfp, 0, sizeof pfp);
memset(&pfr, 0, sizeof pfr);
snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
getpid(), id);
strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE);
strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
switch (rs_num) {
case PF_RULESET_FILTER:
pfr.ticket = pfte[TRANS_FILTER].ticket;
break;
case PF_RULESET_NAT:
pfr.ticket = pfte[TRANS_NAT].ticket;
break;
case PF_RULESET_RDR:
pfr.ticket = pfte[TRANS_RDR].ticket;
break;
default:
errno = EINVAL;
return (-1);
}
if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1)
return (-1);
pfr.pool_ticket = pfp.ticket;
/* Generic for all rule types. */
pfr.rule.af = src->sa_family;
pfr.rule.proto = proto;
pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
if (src->sa_family == AF_INET) {
memcpy(&pfr.rule.src.addr.v.a.addr.v4,
&satosin(src)->sin_addr.s_addr, 4);
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
&satosin(dst)->sin_addr.s_addr, 4);
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
} else {
memcpy(&pfr.rule.src.addr.v.a.addr.v6,
&satosin6(src)->sin6_addr.s6_addr, 16);
memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
&satosin6(dst)->sin6_addr.s6_addr, 16);
memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
}
pfr.rule.dst.port_op = PF_OP_EQ;
pfr.rule.dst.port[0] = htons(d_port);
switch (rs_num) {
case PF_RULESET_FILTER:
/*
* pass quick [log] inet[6] proto tcp \
* from $src to $dst port = $d_port flags S/SAFR keep state
* (max 1) [queue qname]
*/
pfr.rule.action = PF_PASS;
pfr.rule.quick = 1;
pfr.rule.log = rule_log;
pfr.rule.keep_state = 1;
pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL);
pfr.rule.flagset = (proto == IPPROTO_TCP ?
(TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL);
pfr.rule.max_states = 1;
if (qname != NULL)
strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
break;
case PF_RULESET_NAT:
/*
* nat inet[6] proto tcp from $src to $dst port $d_port -> $nat
*/
pfr.rule.action = PF_NAT;
break;
case PF_RULESET_RDR:
/*
* rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr
*/
pfr.rule.action = PF_RDR;
break;
default:
errno = EINVAL;
return (-1);
}
return (0);
}
int
server_lookup(struct sockaddr *client, struct sockaddr *proxy,
struct sockaddr *server, u_int8_t proto)
{
if (client->sa_family == AF_INET)
return (server_lookup4(satosin(client), satosin(proxy),
satosin(server), proto));
if (client->sa_family == AF_INET6)
return (server_lookup6(satosin6(client), satosin6(proxy),
satosin6(server), proto));
errno = EPROTONOSUPPORT;
return (-1);
}
int
server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
struct sockaddr_in *server, u_int8_t proto)
{
struct pfioc_natlook pnl;
memset(&pnl, 0, sizeof pnl);
pnl.direction = PF_OUT;
pnl.af = AF_INET;
pnl.proto = proto;
memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
pnl.sport = client->sin_port;
pnl.dport = proxy->sin_port;
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
return (-1);
memset(server, 0, sizeof(struct sockaddr_in));
server->sin_len = sizeof(struct sockaddr_in);
server->sin_family = AF_INET;
memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
sizeof server->sin_addr.s_addr);
server->sin_port = pnl.rdport;
return (0);
}
int
server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
struct sockaddr_in6 *server, u_int8_t proto)
{
struct pfioc_natlook pnl;
memset(&pnl, 0, sizeof pnl);
pnl.direction = PF_OUT;
pnl.af = AF_INET6;
pnl.proto = proto;
memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
pnl.sport = client->sin6_port;
pnl.dport = proxy->sin6_port;
if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
return (-1);
memset(server, 0, sizeof(struct sockaddr_in6));
server->sin6_len = sizeof(struct sockaddr_in6);
server->sin6_family = AF_INET6;
memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
sizeof server->sin6_addr);
server->sin6_port = pnl.rdport;
return (0);
}

View File

@ -0,0 +1,32 @@
/* $OpenBSD: filter.h,v 1.1 2005/12/28 19:07:07 jcs Exp $ */
/*
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
*
* 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.
*/
#define FTP_PROXY_ANCHOR "tftp-proxy"
int add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *,
u_int16_t, u_int8_t);
int add_nat(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
struct sockaddr *, u_int16_t, u_int16_t, u_int8_t);
int add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
struct sockaddr *, u_int16_t, u_int8_t);
int do_commit(void);
int do_rollback(void);
void init_filter(char *, int);
int prepare_commit(u_int32_t);
int server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *,
u_int8_t);

View File

@ -0,0 +1,140 @@
.\" $OpenBSD: tftp-proxy.8,v 1.1 2005/12/28 19:07:07 jcs Exp $
.\"
.\" Copyright (c) 2005 joshua stein <jcs@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.
.\" 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 28, 2005
.Dt TFTP-PROXY 8
.Os
.Sh NAME
.Nm tftp-proxy
.Nd Internet Trivial File Transfer Protocol proxy
.Sh SYNOPSIS
.Nm tftp-proxy
.Op Fl v
.Op Fl w Ar transwait
.Sh DESCRIPTION
.Nm
is a proxy for the Internet Trivial File Transfer Protocol invoked by
the
.Xr inetd 8
internet server.
TFTP connections should be redirected to the proxy using the
.Xr pf 4
.Ar rdr
command, after which the proxy connects to the server on behalf of
the client.
.Pp
The proxy establishes a
.Xr pf 4
.Ar rdr
rule using the
.Ar anchor
facility to rewrite packets between the client and the server.
Once the rule is established,
.Nm
forwards the initial request from the client to the server to begin the
transfer.
After
.Ar transwait
seconds, the
.Xr pf 4
NAT state is assumed to have been established and the
.Ar rdr
rule is deleted and the program exits.
Once the transfer between the client and the server is completed, the
NAT state will naturally expire.
.Pp
Assuming the TFTP command request is from $client to $server, the
proxy connected to the server using the $proxy source address, and
$port is negotiated,
.Nm
adds the following rule to the anchor:
.Bd -literal -offset indent
rdr proto udp from $server to $proxy port $port -\*(Gt $client
.Ed
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl v
Log the connection and request information to
.Xr syslogd 8 .
.It Fl w Ar transwait
Number of seconds to wait for the data transmission to begin before
removing the
.Xr pf 4
.Ar rdr
rule.
The default is 2 seconds.
.El
.Sh CONFIGURATION
To make use of the proxy,
.Xr pf.conf 5
needs the following rules.
The anchors are mandatory.
Adjust the rules as needed for your configuration.
.Pp
In the NAT section:
.Bd -literal -offset indent
nat on $ext_if from $int_if -\*(Gt ($ext_if:0)
no nat on $ext_if to port tftp
rdr-anchor "tftp-proxy/*"
rdr on $int_if proto udp from $lan to any port tftp -\*(Gt \e
127.0.0.1 port 6969
.Ed
.Pp
In the filter section, an anchor must be added to hold the pass rules:
.Bd -literal -offset indent
anchor "tftp-proxy/*"
.Ed
.Pp
.Xr inetd 8
must be configured to spawn the proxy on the port that packets are
being forwarded to by
.Xr pf 4 .
An example
.Xr inetd.conf 5
entry follows:
.Bd -literal -offset indent
127.0.0.1:6969 dgram udp wait root \e
/usr/libexec/tftp-proxy tftp-proxy
.Ed
.Sh SEE ALSO
.Xr tftp 1 ,
.Xr pf 4 ,
.Xr pf.conf 5 ,
.Xr ftp-proxy 8 ,
.Xr inetd 8 ,
.Xr syslogd 8 ,
.Xr tftpd 8
.Sh CAVEATS
.Nm
chroots to
.Pa /var/empty
and changes to user
.Dq proxy
to drop privileges.

View File

@ -0,0 +1,393 @@
/* $OpenBSD: tftp-proxy.c,v 1.2 2006/12/20 03:33:38 joel Exp $
*
* Copyright (c) 2005 DLS Internet Services
* Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must 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.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/tftp.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include "filter.h"
#define CHROOT_DIR "/var/empty"
#define NOPRIV_USER "proxy"
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
#define DEFTRANSWAIT 2
#define NTOP_BUFS 4
#define PKTSIZE SEGSIZE+4
const char *opcode(int);
const char *sock_ntop(struct sockaddr *);
u_int16_t pick_proxy_port(void);
static void usage(void);
extern char *__progname;
char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
int verbose = 0;
int
main(int argc, char *argv[])
{
int c, fd = 0, on = 1, out_fd = 0, peer, reqsize = 0;
int transwait = DEFTRANSWAIT;
char *p;
struct tftphdr *tp;
struct passwd *pw;
char cbuf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
char req[PKTSIZE];
struct cmsghdr *cmsg;
struct msghdr msg;
struct iovec iov;
struct sockaddr_storage from, proxy, server, proxy_to_server, s_in;
struct sockaddr_in sock_out;
socklen_t j;
in_port_t bindport;
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
while ((c = getopt(argc, argv, "vw:")) != -1)
switch (c) {
case 'v':
verbose++;
break;
case 'w':
transwait = strtoll(optarg, &p, 10);
if (transwait < 1) {
syslog(LOG_ERR, "invalid -w value");
exit(1);
}
break;
default:
usage();
break;
}
/* open /dev/pf */
init_filter(NULL, verbose);
tzset();
pw = getpwnam(NOPRIV_USER);
if (!pw) {
syslog(LOG_ERR, "no such user %s: %m", NOPRIV_USER);
exit(1);
}
if (chroot(CHROOT_DIR) || chdir("/")) {
syslog(LOG_ERR, "chroot %s: %m", CHROOT_DIR);
exit(1);
}
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
syslog(LOG_ERR, "can't revoke privs: %m");
exit(1);
}
/* non-blocking io */
if (ioctl(fd, FIONBIO, &on) < 0) {
syslog(LOG_ERR, "ioctl(FIONBIO): %m");
exit(1);
}
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) {
syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
exit(1);
}
j = sizeof(s_in);
if (getsockname(fd, (struct sockaddr *)&s_in, &j) == -1) {
syslog(LOG_ERR, "getsockname: %m");
exit(1);
}
bindport = ((struct sockaddr_in *)&s_in)->sin_port;
/* req will be pushed back out at the end, unchanged */
j = sizeof(from);
if ((reqsize = recvfrom(fd, req, sizeof(req), MSG_PEEK,
(struct sockaddr *)&from, &j)) < 0) {
syslog(LOG_ERR, "recvfrom: %m");
exit(1);
}
bzero(&msg, sizeof(msg));
iov.iov_base = req;
iov.iov_len = sizeof(req);
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cbuf;
msg.msg_controllen = CMSG_LEN(sizeof(struct sockaddr_storage));
if (recvmsg(fd, &msg, 0) < 0) {
syslog(LOG_ERR, "recvmsg: %m");
exit(1);
}
close(fd);
close(1);
peer = socket(from.ss_family, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m");
exit(1);
}
memset(&s_in, 0, sizeof(s_in));
s_in.ss_family = from.ss_family;
s_in.ss_len = from.ss_len;
/* get local address if possible */
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_RECVDSTADDR) {
memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
CMSG_DATA(cmsg), sizeof(struct in_addr));
break;
}
}
if (bind(peer, (struct sockaddr *)&s_in, s_in.ss_len) < 0) {
syslog(LOG_ERR, "bind: %m");
exit(1);
}
if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
syslog(LOG_ERR, "connect: %m");
exit(1);
}
tp = (struct tftphdr *)req;
if (!(ntohs(tp->th_opcode) == RRQ || ntohs(tp->th_opcode) == WRQ)) {
/* not a tftp request, bail */
if (verbose) {
syslog(LOG_WARNING, "not a valid tftp request");
exit(1);
} else
/* exit 0 so inetd doesn't log anything */
exit(0);
}
j = sizeof(struct sockaddr_storage);
if (getsockname(fd, (struct sockaddr *)&proxy, &j) == -1) {
syslog(LOG_ERR, "getsockname: %m");
exit(1);
}
((struct sockaddr_in *)&proxy)->sin_port = bindport;
/* find the un-rdr'd server and port the client wanted */
if (server_lookup((struct sockaddr *)&from,
(struct sockaddr *)&proxy, (struct sockaddr *)&server,
IPPROTO_UDP) != 0) {
syslog(LOG_ERR, "pf connection lookup failed (no rdr?)");
exit(1);
}
/* establish a new outbound connection to the remote server */
if ((out_fd = socket(((struct sockaddr *)&from)->sa_family,
SOCK_DGRAM, IPPROTO_UDP)) < 0) {
syslog(LOG_ERR, "couldn't create new socket");
exit(1);
}
bzero((char *)&sock_out, sizeof(sock_out));
sock_out.sin_family = from.ss_family;
sock_out.sin_port = htons(pick_proxy_port());
if (bind(out_fd, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
syslog(LOG_ERR, "couldn't bind to new socket: %m");
exit(1);
}
if (connect(out_fd, (struct sockaddr *)&server,
((struct sockaddr *)&server)->sa_len) < 0 && errno != EINPROGRESS) {
syslog(LOG_ERR, "couldn't connect to remote server: %m");
exit(1);
}
j = sizeof(struct sockaddr_storage);
if ((getsockname(out_fd, (struct sockaddr *)&proxy_to_server,
&j)) < 0) {
syslog(LOG_ERR, "getsockname: %m");
exit(1);
}
if (verbose)
syslog(LOG_INFO, "%s:%d -> %s:%d/%s:%d -> %s:%d \"%s %s\"",
sock_ntop((struct sockaddr *)&from),
ntohs(((struct sockaddr_in *)&from)->sin_port),
sock_ntop((struct sockaddr *)&proxy),
ntohs(((struct sockaddr_in *)&proxy)->sin_port),
sock_ntop((struct sockaddr *)&proxy_to_server),
ntohs(((struct sockaddr_in *)&proxy_to_server)->sin_port),
sock_ntop((struct sockaddr *)&server),
ntohs(((struct sockaddr_in *)&server)->sin_port),
opcode(ntohs(tp->th_opcode)),
tp->th_stuff);
/* get ready to add rdr and pass rules */
if (prepare_commit(1) == -1) {
syslog(LOG_ERR, "couldn't prepare pf commit");
exit(1);
}
/* rdr from server to us on our random port -> client on its port */
if (add_rdr(1, (struct sockaddr *)&server,
(struct sockaddr *)&proxy_to_server, ntohs(sock_out.sin_port),
(struct sockaddr *)&from,
ntohs(((struct sockaddr_in *)&from)->sin_port),
IPPROTO_UDP) == -1) {
syslog(LOG_ERR, "couldn't add rdr");
exit(1);
}
/* explicitly allow the packets to return back to the client (which pf
* will see post-rdr) */
if (add_filter(1, PF_IN, (struct sockaddr *)&server,
(struct sockaddr *)&from,
ntohs(((struct sockaddr_in *)&from)->sin_port),
IPPROTO_UDP) == -1) {
syslog(LOG_ERR, "couldn't add pass in");
exit(1);
}
if (add_filter(1, PF_OUT, (struct sockaddr *)&server,
(struct sockaddr *)&from,
ntohs(((struct sockaddr_in *)&from)->sin_port),
IPPROTO_UDP) == -1) {
syslog(LOG_ERR, "couldn't add pass out");
exit(1);
}
/* and just in case, to pass out from us to the server */
if (add_filter(1, PF_OUT, (struct sockaddr *)&proxy_to_server,
(struct sockaddr *)&server,
ntohs(((struct sockaddr_in *)&server)->sin_port),
IPPROTO_UDP) == -1) {
syslog(LOG_ERR, "couldn't add pass out");
exit(1);
}
if (do_commit() == -1) {
syslog(LOG_ERR, "couldn't commit pf rules");
exit(1);
}
/* forward the initial tftp request and start the insanity */
if (send(out_fd, tp, reqsize, 0) < 0) {
syslog(LOG_ERR, "couldn't forward tftp packet: %m");
exit(1);
}
/* allow the transfer to start to establish a state */
sleep(transwait);
/* delete our rdr rule and clean up */
prepare_commit(1);
do_commit();
return(0);
}
const char *
opcode(int code)
{
static char str[6];
switch (code) {
case 1:
(void)snprintf(str, sizeof(str), "RRQ");
break;
case 2:
(void)snprintf(str, sizeof(str), "WRQ");
break;
default:
(void)snprintf(str, sizeof(str), "(%d)", code);
break;
}
return (str);
}
const char *
sock_ntop(struct sockaddr *sa)
{
static int n = 0;
/* Cycle to next buffer. */
n = (n + 1) % NTOP_BUFS;
ntop_buf[n][0] = '\0';
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
sizeof ntop_buf[0]));
}
if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
sizeof ntop_buf[0]));
}
return (NULL);
}
u_int16_t
pick_proxy_port(void)
{
return (IPPORT_HIFIRSTAUTO + (arc4random() %
(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)));
}
static void
usage(void)
{
syslog(LOG_ERR, "usage: %s [-v] [-w transwait]", __progname);
exit(1);
}