PF from OpenBSD 3.5

This commit is contained in:
itojun 2004-06-22 13:52:05 +00:00
parent eaa9fc4cc3
commit 6adffbf983
11 changed files with 16821 additions and 0 deletions

229
sys/dist/pf/net/if_pflog.c vendored Normal file
View File

@ -0,0 +1,229 @@
/* $OpenBSD: if_pflog.c,v 1.11 2003/12/31 11:18:25 cedric Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* This code was written by John Ioannidis for BSD/OS in Athens, Greece,
* in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
* and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis, Niels Provos.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include "bpfilter.h"
#include "pflog.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#endif
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <netinet6/nd6.h>
#endif /* INET6 */
#include <net/pfvar.h>
#include <net/if_pflog.h>
#define PFLOGMTU (32768 + MHLEN + MLEN)
#ifdef PFLOGDEBUG
#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
struct pflog_softc pflogif[NPFLOG];
void pflogattach(int);
int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int pflogioctl(struct ifnet *, u_long, caddr_t);
void pflogrtrequest(int, struct rtentry *, struct sockaddr *);
void pflogstart(struct ifnet *);
extern int ifqmaxlen;
void
pflogattach(int npflog)
{
struct ifnet *ifp;
int i;
bzero(pflogif, sizeof(pflogif));
for (i = 0; i < NPFLOG; i++) {
ifp = &pflogif[i].sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", i);
ifp->if_softc = &pflogif[i];
ifp->if_mtu = PFLOGMTU;
ifp->if_ioctl = pflogioctl;
ifp->if_output = pflogoutput;
ifp->if_start = pflogstart;
ifp->if_type = IFT_PFLOG;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_hdrlen = PFLOG_HDRLEN;
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&pflogif[i].sc_if.if_bpf, ifp, DLT_PFLOG,
PFLOG_HDRLEN);
#endif
}
}
/*
* Start output on the pflog interface.
*/
void
pflogstart(struct ifnet *ifp)
{
struct mbuf *m;
int s;
for (;;) {
s = splimp();
IF_DROP(&ifp->if_snd);
IF_DEQUEUE(&ifp->if_snd, m);
splx(s);
if (m == NULL)
return;
else
m_freem(m);
}
}
int
pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m);
return (0);
}
/* ARGSUSED */
void
pflogrtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa)
{
if (rt)
rt->rt_rmx.rmx_mtu = PFLOGMTU;
}
/* ARGSUSED */
int
pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
switch (cmd) {
case SIOCSIFADDR:
case SIOCAIFADDR:
case SIOCSIFDSTADDR:
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
break;
default:
return (EINVAL);
}
return (0);
}
int
pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir,
u_int8_t reason, struct pf_rule *rm, struct pf_rule *am,
struct pf_ruleset *ruleset)
{
#if NBPFILTER > 0
struct ifnet *ifn;
struct pfloghdr hdr;
struct mbuf m1;
if (kif == NULL || m == NULL || rm == NULL)
return (-1);
bzero(&hdr, sizeof(hdr));
hdr.length = PFLOG_REAL_HDRLEN;
hdr.af = af;
hdr.action = rm->action;
hdr.reason = reason;
memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname));
if (am == NULL) {
hdr.rulenr = htonl(rm->nr);
hdr.subrulenr = -1;
} else {
hdr.rulenr = htonl(am->nr);
hdr.subrulenr = htonl(rm->nr);
if (ruleset != NULL)
memcpy(hdr.ruleset, ruleset->name,
sizeof(hdr.ruleset));
}
hdr.dir = dir;
#ifdef INET
if (af == AF_INET && dir == PF_OUT) {
struct ip *ip;
ip = mtod(m, struct ip *);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
}
#endif /* INET */
m1.m_next = m;
m1.m_len = PFLOG_HDRLEN;
m1.m_data = (char *) &hdr;
ifn = &(pflogif[0].sc_if);
if (ifn->if_bpf)
bpf_mtap(ifn->if_bpf, &m1);
#endif
return (0);
}

75
sys/dist/pf/net/if_pflog.h vendored Normal file
View File

@ -0,0 +1,75 @@
/* $OpenBSD: if_pflog.h,v 1.10 2004/03/19 04:52:04 frantzen Exp $ */
/*
* Copyright 2001 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NET_IF_PFLOG_H_
#define _NET_IF_PFLOG_H_
struct pflog_softc {
struct ifnet sc_if; /* the interface */
};
/* XXX keep in sync with pfvar.h */
#ifndef PF_RULESET_NAME_SIZE
#define PF_RULESET_NAME_SIZE 16
#endif
struct pfloghdr {
u_int8_t length;
sa_family_t af;
u_int8_t action;
u_int8_t reason;
char ifname[IFNAMSIZ];
char ruleset[PF_RULESET_NAME_SIZE];
u_int32_t rulenr;
u_int32_t subrulenr;
u_int8_t dir;
u_int8_t pad[3];
};
#define PFLOG_HDRLEN sizeof(struct pfloghdr)
/* minus pad, also used as a signature */
#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad)
/* XXX remove later when old format logs are no longer needed */
struct old_pfloghdr {
u_int32_t af;
char ifname[IFNAMSIZ];
short rnr;
u_short reason;
u_short action;
u_short dir;
};
#define OLD_PFLOG_HDRLEN sizeof(struct old_pfloghdr)
#ifdef _KERNEL
#if NPFLOG > 0
#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g) pflog_packet(i,a,b,c,d,e,f,g)
#else
#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g) ((void)0)
#endif /* NPFLOG > 0 */
#endif /* _KERNEL */
#endif /* _NET_IF_PFLOG_H_ */

1243
sys/dist/pf/net/if_pfsync.c vendored Normal file

File diff suppressed because it is too large Load Diff

280
sys/dist/pf/net/if_pfsync.h vendored Normal file
View File

@ -0,0 +1,280 @@
/* $OpenBSD: if_pfsync.h,v 1.13 2004/03/22 04:54:17 mcbride Exp $ */
/*
* Copyright (c) 2001 Michael Shalayeff
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NET_IF_PFSYNC_H_
#define _NET_IF_PFSYNC_H_
#define PFSYNC_ID_LEN sizeof(u_int64_t)
struct pfsync_state_scrub {
u_int16_t pfss_flags;
u_int8_t pfss_ttl; /* stashed TTL */
u_int8_t scrub_flag;
u_int32_t pfss_ts_mod; /* timestamp modulation */
} __packed;
struct pfsync_state_host {
struct pf_addr addr;
u_int16_t port;
u_int16_t pad[3];
} __packed;
struct pfsync_state_peer {
struct pfsync_state_scrub scrub; /* state is scrubbed */
u_int32_t seqlo; /* Max sequence number sent */
u_int32_t seqhi; /* Max the other end ACKd + win */
u_int32_t seqdiff; /* Sequence number modulator */
u_int16_t max_win; /* largest window (pre scaling) */
u_int16_t mss; /* Maximum segment size option */
u_int8_t state; /* active state level */
u_int8_t wscale; /* window scaling factor */
u_int8_t scrub_flag;
u_int8_t pad[5];
} __packed;
struct pfsync_state {
u_int32_t id[2];
char ifname[IFNAMSIZ];
struct pfsync_state_host lan;
struct pfsync_state_host gwy;
struct pfsync_state_host ext;
struct pfsync_state_peer src;
struct pfsync_state_peer dst;
struct pf_addr rt_addr;
u_int32_t rule;
u_int32_t anchor;
u_int32_t nat_rule;
u_int32_t creation;
u_int32_t expire;
u_int32_t packets[2];
u_int32_t bytes[2];
u_int32_t creatorid;
sa_family_t af;
u_int8_t proto;
u_int8_t direction;
u_int8_t log;
u_int8_t allow_opts;
u_int8_t timeout;
u_int8_t sync_flags;
u_int8_t updates;
} __packed;
struct pfsync_state_upd {
u_int32_t id[2];
struct pfsync_state_peer src;
struct pfsync_state_peer dst;
u_int32_t creatorid;
u_int32_t expire;
u_int8_t timeout;
u_int8_t updates;
u_int8_t pad[6];
} __packed;
struct pfsync_state_del {
u_int32_t id[2];
u_int32_t creatorid;
struct {
u_int8_t state;
} src;
struct {
u_int8_t state;
} dst;
u_int8_t pad[2];
} __packed;
struct pfsync_state_upd_req {
u_int32_t id[2];
u_int32_t creatorid;
u_int32_t pad;
} __packed;
struct pfsync_state_clr {
char ifname[IFNAMSIZ];
u_int32_t creatorid;
u_int32_t pad;
} __packed;
struct pfsync_state_bus {
u_int32_t creatorid;
u_int32_t endtime;
u_int8_t status;
#define PFSYNC_BUS_START 1
#define PFSYNC_BUS_END 2
u_int8_t pad[7];
} __packed;
#ifdef _KERNEL
union sc_statep {
struct pfsync_state *s;
struct pfsync_state_upd *u;
struct pfsync_state_del *d;
struct pfsync_state_clr *c;
struct pfsync_state_bus *b;
struct pfsync_state_upd_req *r;
};
extern int pfsync_sync_ok;
struct pfsync_softc {
struct ifnet sc_if;
struct ifnet *sc_sync_ifp;
struct ip_moptions sc_imo;
struct timeout sc_tmo;
struct timeout sc_bulk_tmo;
struct timeout sc_bulkfail_tmo;
struct in_addr sc_sendaddr;
struct mbuf *sc_mbuf; /* current cummulative mbuf */
struct mbuf *sc_mbuf_net; /* current cummulative mbuf */
union sc_statep sc_statep;
union sc_statep sc_statep_net;
u_int32_t sc_ureq_received;
u_int32_t sc_ureq_sent;
int sc_bulk_tries;
int sc_maxcount; /* number of states in mtu */
int sc_maxupdates; /* number of updates/state */
};
#endif
struct pfsync_header {
u_int8_t version;
#define PFSYNC_VERSION 2
u_int8_t af;
u_int8_t action;
#define PFSYNC_ACT_CLR 0 /* clear all states */
#define PFSYNC_ACT_INS 1 /* insert state */
#define PFSYNC_ACT_UPD 2 /* update state */
#define PFSYNC_ACT_DEL 3 /* delete state */
#define PFSYNC_ACT_UPD_C 4 /* "compressed" state update */
#define PFSYNC_ACT_DEL_C 5 /* "compressed" state delete */
#define PFSYNC_ACT_INS_F 6 /* insert fragment */
#define PFSYNC_ACT_DEL_F 7 /* delete fragments */
#define PFSYNC_ACT_UREQ 8 /* request "uncompressed" state */
#define PFSYNC_ACT_BUS 9 /* Bulk Update Status */
#define PFSYNC_ACT_MAX 10
u_int8_t count;
} __packed;
#define PFSYNC_BULKPACKETS 1 /* # of packets per timeout */
#define PFSYNC_MAX_BULKTRIES 12
#define PFSYNC_HDRLEN sizeof(struct pfsync_header)
#define PFSYNC_ACTIONS \
"CLR ST", "INS ST", "UPD ST", "DEL ST", \
"UPD ST COMP", "DEL ST COMP", "INS FR", "DEL FR", \
"UPD REQ", "BLK UPD STAT"
#define PFSYNC_DFLTTL 255
struct pfsyncstats {
u_long pfsyncs_ipackets; /* total input packets, IPv4 */
u_long pfsyncs_ipackets6; /* total input packets, IPv6 */
u_long pfsyncs_badif; /* not the right interface */
u_long pfsyncs_badttl; /* TTL is not PFSYNC_DFLTTL */
u_long pfsyncs_hdrops; /* packets shorter than header */
u_long pfsyncs_badver; /* bad (incl unsupp) version */
u_long pfsyncs_badact; /* bad action */
u_long pfsyncs_badlen; /* data length does not match */
u_long pfsyncs_badauth; /* bad authentication */
u_long pfsyncs_badstate; /* insert/lookup failed */
u_long pfsyncs_opackets; /* total output packets, IPv4 */
u_long pfsyncs_opackets6; /* total output packets, IPv6 */
u_long pfsyncs_onomem; /* no memory for an mbuf for a send */
u_long pfsyncs_oerrors; /* ip output error */
};
/*
* Configuration structure for SIOCSETPFSYNC SIOCGETPFSYNC
*/
struct pfsyncreq {
char pfsyncr_syncif[IFNAMSIZ];
int pfsyncr_maxupdates;
int pfsyncr_authlevel;
};
#define SIOCSETPFSYNC _IOW('i', 247, struct ifreq)
#define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq)
#define pf_state_peer_hton(s,d) do { \
(d)->seqlo = htonl((s)->seqlo); \
(d)->seqhi = htonl((s)->seqhi); \
(d)->seqdiff = htonl((s)->seqdiff); \
(d)->max_win = htons((s)->max_win); \
(d)->mss = htons((s)->mss); \
(d)->state = (s)->state; \
(d)->wscale = (s)->wscale; \
} while (0)
#define pf_state_peer_ntoh(s,d) do { \
(d)->seqlo = ntohl((s)->seqlo); \
(d)->seqhi = ntohl((s)->seqhi); \
(d)->seqdiff = ntohl((s)->seqdiff); \
(d)->max_win = ntohs((s)->max_win); \
(d)->mss = ntohs((s)->mss); \
(d)->state = (s)->state; \
(d)->wscale = (s)->wscale; \
} while (0)
#define pf_state_host_hton(s,d) do { \
bcopy(&(s)->addr, &(d)->addr, sizeof((d)->addr)); \
(d)->port = (s)->port; \
} while (0)
#define pf_state_host_ntoh(s,d) do { \
bcopy(&(s)->addr, &(d)->addr, sizeof((d)->addr)); \
(d)->port = (s)->port; \
} while (0)
#ifdef _KERNEL
void pfsync_input(struct mbuf *, ...);
int pfsync_clear_states(u_int32_t, char *);
int pfsync_pack_state(u_int8_t, struct pf_state *, int);
#define pfsync_insert_state(st) do { \
if ((st->rule.ptr->rule_flag & PFRULE_NOSYNC) || \
(st->proto == IPPROTO_PFSYNC)) \
st->sync_flags |= PFSTATE_NOSYNC; \
else if (!st->sync_flags) \
pfsync_pack_state(PFSYNC_ACT_INS, (st), 1); \
st->sync_flags &= ~PFSTATE_FROMSYNC; \
} while (0)
#define pfsync_update_state(st) do { \
if (!st->sync_flags) \
pfsync_pack_state(PFSYNC_ACT_UPD, (st), 1); \
st->sync_flags &= ~PFSTATE_FROMSYNC; \
} while (0)
#define pfsync_delete_state(st) do { \
if (!st->sync_flags) \
pfsync_pack_state(PFSYNC_ACT_DEL, (st), 1); \
st->sync_flags &= ~PFSTATE_FROMSYNC; \
} while (0)
#endif
#endif /* _NET_IF_PFSYNC_H_ */

5854
sys/dist/pf/net/pf.c vendored Normal file

File diff suppressed because it is too large Load Diff

840
sys/dist/pf/net/pf_if.c vendored Normal file
View File

@ -0,0 +1,840 @@
/* $OpenBSD: pf_if.c,v 1.11 2004/03/15 11:38:23 cedric Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2003 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/filio.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <net/pfvar.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#define ACCEPT_FLAGS(oklist) \
do { \
if ((flags & ~(oklist)) & \
PFI_FLAG_ALLMASK) \
return (EINVAL); \
} while (0)
#define senderr(e) do { rv = (e); goto _bad; } while (0)
struct pfi_kif **pfi_index2kif;
struct pfi_kif *pfi_self;
int pfi_indexlim;
struct pfi_ifhead pfi_ifs;
struct pfi_statehead pfi_statehead;
int pfi_ifcnt;
struct pool pfi_addr_pl;
long pfi_update = 1;
struct pfr_addr *pfi_buffer;
int pfi_buffer_cnt;
int pfi_buffer_max;
char pfi_reserved_anchor[PF_ANCHOR_NAME_SIZE] =
PF_RESERVED_ANCHOR;
char pfi_interface_ruleset[PF_RULESET_NAME_SIZE] =
PF_INTERFACE_RULESET;
void pfi_dynaddr_update(void *);
void pfi_kifaddr_update(void *);
void pfi_table_update(struct pfr_ktable *, struct pfi_kif *,
int, int);
void pfi_instance_add(struct ifnet *, int, int);
void pfi_address_add(struct sockaddr *, int, int);
int pfi_if_compare(struct pfi_kif *, struct pfi_kif *);
struct pfi_kif *pfi_if_create(const char *, struct pfi_kif *, int);
void pfi_copy_group(char *, const char *, int);
void pfi_dynamic_drivers(void);
void pfi_newgroup(const char *, int);
int pfi_skip_if(const char *, struct pfi_kif *, int);
int pfi_unmask(void *);
void pfi_dohooks(struct pfi_kif *);
RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
#define PFI_DYNAMIC_BUSES { "pcmcia", "cardbus", "uhub" }
#define PFI_BUFFER_MAX 0x10000
#define PFI_MTYPE M_IFADDR
void
pfi_initialize(void)
{
if (pfi_self != NULL) /* already initialized */
return;
TAILQ_INIT(&pfi_statehead);
pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, 0, 0,
"pfiaddrpl", &pool_allocator_nointr);
pfi_buffer_max = 64;
pfi_buffer = malloc(pfi_buffer_max * sizeof(*pfi_buffer),
PFI_MTYPE, M_WAITOK);
pfi_self = pfi_if_create("self", NULL, PFI_IFLAG_GROUP);
pfi_dynamic_drivers();
}
void
pfi_attach_clone(struct if_clone *ifc)
{
pfi_initialize();
pfi_newgroup(ifc->ifc_name, PFI_IFLAG_CLONABLE);
}
void
pfi_attach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *p, *q, key;
int s;
pfi_initialize();
s = splsoftnet();
pfi_update++;
if (ifp->if_index >= pfi_indexlim) {
/*
* grow pfi_index2kif, similar to ifindex2ifnet code in if.c
*/
size_t m, n, oldlim;
struct pfi_kif **mp, **np;
oldlim = pfi_indexlim;
if (pfi_indexlim == 0)
pfi_indexlim = 64;
while (ifp->if_index >= pfi_indexlim)
pfi_indexlim <<= 1;
m = oldlim * sizeof(struct pfi_kif *);
mp = pfi_index2kif;
n = pfi_indexlim * sizeof(struct pfi_kif *);
np = malloc(n, PFI_MTYPE, M_DONTWAIT);
if (np == NULL)
panic("pfi_attach_ifnet: "
"cannot allocate translation table");
bzero(np, n);
if (mp != NULL)
bcopy(mp, np, m);
pfi_index2kif = np;
if (mp != NULL)
free(mp, PFI_MTYPE);
}
strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name));
p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
if (p == NULL) {
/* add group */
pfi_copy_group(key.pfik_name, ifp->if_xname,
sizeof(key.pfik_name));
q = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
if (q == NULL)
q = pfi_if_create(key.pfik_name, pfi_self, PFI_IFLAG_GROUP);
if (q == NULL)
panic("pfi_attach_ifnet: "
"cannot allocate '%s' group", key.pfik_name);
/* add interface */
p = pfi_if_create(ifp->if_xname, q, PFI_IFLAG_INSTANCE);
if (p == NULL)
panic("pfi_attach_ifnet: "
"cannot allocate '%s' interface", ifp->if_xname);
} else
q = p->pfik_parent;
p->pfik_ifp = ifp;
p->pfik_flags |= PFI_IFLAG_ATTACHED;
p->pfik_ah_cookie =
hook_establish(ifp->if_addrhooks, 1, pfi_kifaddr_update, p);
pfi_index2kif[ifp->if_index] = p;
pfi_dohooks(p);
splx(s);
}
void
pfi_detach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *p, *q, key;
int s;
strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name));
s = splsoftnet();
pfi_update++;
p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
if (p == NULL) {
printf("pfi_detach_ifnet: cannot find %s", ifp->if_xname);
splx(s);
return;
}
hook_disestablish(p->pfik_ifp->if_addrhooks, p->pfik_ah_cookie);
q = p->pfik_parent;
p->pfik_ifp = NULL;
p->pfik_flags &= ~PFI_IFLAG_ATTACHED;
pfi_index2kif[ifp->if_index] = NULL;
pfi_dohooks(p);
pfi_maybe_destroy(p);
splx(s);
}
struct pfi_kif *
pfi_lookup_create(const char *name)
{
struct pfi_kif *p, *q, key;
int s;
s = splsoftnet();
p = pfi_lookup_if(name);
if (p == NULL) {
pfi_copy_group(key.pfik_name, name, sizeof(key.pfik_name));
q = pfi_lookup_if(key.pfik_name);
if (q != NULL)
p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE);
}
splx(s);
return (p);
}
struct pfi_kif *
pfi_attach_rule(const char *name)
{
struct pfi_kif *p;
p = pfi_lookup_create(name);
if (p != NULL)
p->pfik_rules++;
return (p);
}
void
pfi_detach_rule(struct pfi_kif *p)
{
if (p == NULL)
return;
if (p->pfik_rules > 0)
p->pfik_rules--;
else
printf("pfi_detach_rule: reference count at 0\n");
pfi_maybe_destroy(p);
}
void
pfi_attach_state(struct pfi_kif *p)
{
if (!p->pfik_states++)
TAILQ_INSERT_TAIL(&pfi_statehead, p, pfik_w_states);
}
void
pfi_detach_state(struct pfi_kif *p)
{
if (p == NULL)
return;
if (p->pfik_states <= 0) {
printf("pfi_detach_state: reference count <= 0\n");
return;
}
if (!--p->pfik_states)
TAILQ_REMOVE(&pfi_statehead, p, pfik_w_states);
pfi_maybe_destroy(p);
}
int
pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
{
struct pfi_dynaddr *dyn;
char tblname[PF_TABLE_NAME_SIZE];
struct pf_ruleset *ruleset = NULL;
int s, rv = 0;
if (aw->type != PF_ADDR_DYNIFTL)
return (0);
dyn = pool_get(&pfi_addr_pl, PR_NOWAIT);
if (dyn == NULL)
return (1);
bzero(dyn, sizeof(*dyn));
s = splsoftnet();
dyn->pfid_kif = pfi_attach_rule(aw->v.ifname);
if (dyn->pfid_kif == NULL)
senderr(1);
dyn->pfid_net = pfi_unmask(&aw->v.a.mask);
if (af == AF_INET && dyn->pfid_net == 32)
dyn->pfid_net = 128;
strlcpy(tblname, aw->v.ifname, sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NETWORK)
strlcat(tblname, ":network", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_BROADCAST)
strlcat(tblname, ":broadcast", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_PEER)
strlcat(tblname, ":peer", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NOALIAS)
strlcat(tblname, ":0", sizeof(tblname));
if (dyn->pfid_net != 128)
snprintf(tblname + strlen(tblname),
sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net);
ruleset = pf_find_or_create_ruleset(pfi_reserved_anchor,
pfi_interface_ruleset);
if (ruleset == NULL)
senderr(1);
dyn->pfid_kt = pfr_attach_table(ruleset, tblname);
if (dyn->pfid_kt == NULL)
senderr(1);
dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE;
dyn->pfid_iflags = aw->iflags;
dyn->pfid_af = af;
dyn->pfid_hook_cookie = hook_establish(dyn->pfid_kif->pfik_ah_head, 1,
pfi_dynaddr_update, dyn);
if (dyn->pfid_hook_cookie == NULL)
senderr(1);
aw->p.dyn = dyn;
pfi_dynaddr_update(aw->p.dyn);
splx(s);
return (0);
_bad:
if (dyn->pfid_kt != NULL)
pfr_detach_table(dyn->pfid_kt);
if (ruleset != NULL)
pf_remove_if_empty_ruleset(ruleset);
if (dyn->pfid_kif != NULL)
pfi_detach_rule(dyn->pfid_kif);
pool_put(&pfi_addr_pl, dyn);
splx(s);
return (rv);
}
void
pfi_dynaddr_update(void *p)
{
struct pfi_dynaddr *dyn = (struct pfi_dynaddr *)p;
struct pfi_kif *kif = dyn->pfid_kif;
struct pfr_ktable *kt = dyn->pfid_kt;
if (dyn == NULL || kif == NULL || kt == NULL)
panic("pfi_dynaddr_update");
if (kt->pfrkt_larg != pfi_update) {
/* this table needs to be brought up-to-date */
pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags);
kt->pfrkt_larg = pfi_update;
}
pfr_dynaddr_update(kt, dyn);
}
void
pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags)
{
int e, size2 = 0;
struct pfi_kif *p;
struct pfr_table t;
if ((kif->pfik_flags & PFI_IFLAG_INSTANCE) && kif->pfik_ifp == NULL) {
pfr_clr_addrs(&kt->pfrkt_t, NULL, 0);
return;
}
pfi_buffer_cnt = 0;
if ((kif->pfik_flags & PFI_IFLAG_INSTANCE))
pfi_instance_add(kif->pfik_ifp, net, flags);
else if (strcmp(kif->pfik_name, "self")) {
TAILQ_FOREACH(p, &kif->pfik_grouphead, pfik_instances)
pfi_instance_add(p->pfik_ifp, net, flags);
} else {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
if (p->pfik_flags & PFI_IFLAG_INSTANCE)
pfi_instance_add(p->pfik_ifp, net, flags);
}
t = kt->pfrkt_t;
t.pfrt_flags = 0;
if ((e = pfr_set_addrs(&t, pfi_buffer, pfi_buffer_cnt, &size2,
NULL, NULL, NULL, 0)))
printf("pfi_table_update: cannot set %d new addresses "
"into table %s: %d\n", pfi_buffer_cnt, kt->pfrkt_name, e);
}
void
pfi_instance_add(struct ifnet *ifp, int net, int flags)
{
struct ifaddr *ia;
int got4 = 0, got6 = 0;
int net2, af;
if (ifp == NULL)
return;
TAILQ_FOREACH(ia, &ifp->if_addrlist, ifa_list) {
if (ia->ifa_addr == NULL)
continue;
af = ia->ifa_addr->sa_family;
if (af != AF_INET && af != AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) &&
!(ifp->if_flags & IFF_BROADCAST))
continue;
if ((flags & PFI_AFLAG_PEER) &&
!(ifp->if_flags & IFF_POINTOPOINT))
continue;
if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(
&((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr))
continue;
if (flags & PFI_AFLAG_NOALIAS) {
if (af == AF_INET && got4)
continue;
if (af == AF_INET6 && got6)
continue;
}
if (af == AF_INET)
got4 = 1;
else
got6 = 1;
net2 = net;
if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) {
if (af == AF_INET) {
net2 = pfi_unmask(&((struct sockaddr_in *)
ia->ifa_netmask)->sin_addr);
} else {
net2 = pfi_unmask(&((struct sockaddr_in6 *)
ia->ifa_netmask)->sin6_addr);
}
}
if (af == AF_INET && net2 > 32)
net2 = 32;
if (flags & PFI_AFLAG_BROADCAST)
pfi_address_add(ia->ifa_broadaddr, af, net2);
else if (flags & PFI_AFLAG_PEER)
pfi_address_add(ia->ifa_dstaddr, af, net2);
else
pfi_address_add(ia->ifa_addr, af, net2);
}
}
void
pfi_address_add(struct sockaddr *sa, int af, int net)
{
struct pfr_addr *p;
int i;
if (pfi_buffer_cnt >= pfi_buffer_max) {
int new_max = pfi_buffer_max * 2;
if (new_max > PFI_BUFFER_MAX) {
printf("pfi_address_add: address buffer full (%d/%d)\n",
pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE,
M_DONTWAIT);
if (p == NULL) {
printf("pfi_address_add: no memory to grow buffer "
"(%d/%d)\n", pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
memcpy(pfi_buffer, p, pfi_buffer_cnt * sizeof(*pfi_buffer));
/* no need to zero buffer */
free(pfi_buffer, PFI_MTYPE);
pfi_buffer = p;
pfi_buffer_max = new_max;
}
if (af == AF_INET && net > 32)
net = 128;
p = pfi_buffer + pfi_buffer_cnt++;
bzero(p, sizeof(*p));
p->pfra_af = af;
p->pfra_net = net;
if (af == AF_INET)
p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr;
if (af == AF_INET6) {
p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
if (IN6_IS_ADDR_LINKLOCAL(&p->pfra_ip6addr))
p->pfra_ip6addr.s6_addr16[1] = 0;
}
/* mask network address bits */
if (net < 128)
((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8));
for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++)
((caddr_t)p)[i] = 0;
}
void
pfi_dynaddr_remove(struct pf_addr_wrap *aw)
{
int s;
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL)
return;
s = splsoftnet();
hook_disestablish(aw->p.dyn->pfid_kif->pfik_ah_head,
aw->p.dyn->pfid_hook_cookie);
pfi_detach_rule(aw->p.dyn->pfid_kif);
aw->p.dyn->pfid_kif = NULL;
pfr_detach_table(aw->p.dyn->pfid_kt);
aw->p.dyn->pfid_kt = NULL;
pool_put(&pfi_addr_pl, aw->p.dyn);
aw->p.dyn = NULL;
splx(s);
}
void
pfi_dynaddr_copyout(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL)
return;
aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6;
}
void
pfi_kifaddr_update(void *v)
{
int s;
s = splsoftnet();
pfi_update++;
pfi_dohooks(v);
splx(s);
}
int
pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q)
{
return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ));
}
struct pfi_kif *
pfi_if_create(const char *name, struct pfi_kif *q, int flags)
{
struct pfi_kif *p;
p = malloc(sizeof(*p), PFI_MTYPE, M_DONTWAIT);
if (p == NULL)
return (NULL);
bzero(p, sizeof(*p));
p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE,
M_DONTWAIT);
if (p->pfik_ah_head == NULL) {
free(p, PFI_MTYPE);
return (NULL);
}
bzero(p->pfik_ah_head, sizeof(*p->pfik_ah_head));
TAILQ_INIT(p->pfik_ah_head);
TAILQ_INIT(&p->pfik_grouphead);
strlcpy(p->pfik_name, name, sizeof(p->pfik_name));
RB_INIT(&p->pfik_lan_ext);
RB_INIT(&p->pfik_ext_gwy);
p->pfik_flags = flags;
p->pfik_parent = q;
p->pfik_tzero = time.tv_sec;
RB_INSERT(pfi_ifhead, &pfi_ifs, p);
if (q != NULL) {
q->pfik_addcnt++;
TAILQ_INSERT_TAIL(&q->pfik_grouphead, p, pfik_instances);
}
pfi_ifcnt++;
return (p);
}
int
pfi_maybe_destroy(struct pfi_kif *p)
{
int i, j, k, s;
struct pfi_kif *q = p->pfik_parent;
if ((p->pfik_flags & (PFI_IFLAG_ATTACHED | PFI_IFLAG_GROUP)) ||
p->pfik_rules > 0 || p->pfik_states > 0)
return (0);
s = splsoftnet();
if (q != NULL) {
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
for (k = 0; k < 2; k++) {
q->pfik_bytes[i][j][k] +=
p->pfik_bytes[i][j][k];
q->pfik_packets[i][j][k] +=
p->pfik_packets[i][j][k];
}
q->pfik_delcnt++;
TAILQ_REMOVE(&q->pfik_grouphead, p, pfik_instances);
}
pfi_ifcnt--;
RB_REMOVE(pfi_ifhead, &pfi_ifs, p);
splx(s);
free(p->pfik_ah_head, PFI_MTYPE);
free(p, PFI_MTYPE);
return (1);
}
void
pfi_copy_group(char *p, const char *q, int m)
{
while (m > 1 && *q && !(*q >= '0' && *q <= '9')) {
*p++ = *q++;
m--;
}
if (m > 0)
*p++ = '\0';
}
void
pfi_dynamic_drivers(void)
{
char *buses[] = PFI_DYNAMIC_BUSES;
int nbuses = sizeof(buses)/sizeof(buses[0]);
int enabled[sizeof(buses)/sizeof(buses[0])];
struct device *dev;
struct cfdata *cf;
struct cfdriver *drv;
short *p;
int i;
bzero(enabled, sizeof(enabled));
TAILQ_FOREACH(dev, &alldevs, dv_list) {
if (!(dev->dv_flags & DVF_ACTIVE))
continue;
for (i = 0; i < nbuses; i++)
if (!enabled[i] && !strcmp(buses[i],
dev->dv_cfdata->cf_driver->cd_name))
enabled[i] = 1;
}
for (cf = cfdata; cf->cf_driver; cf++) {
if (cf->cf_driver->cd_class != DV_IFNET)
continue;
for (p = cf->cf_parents; p && *p >= 0; p++) {
if ((drv = cfdata[*p].cf_driver) == NULL)
continue;
for (i = 0; i < nbuses; i++)
if (enabled[i] &&
!strcmp(drv->cd_name, buses[i]))
break;
if (i < nbuses) {
pfi_newgroup(cf->cf_driver->cd_name,
PFI_IFLAG_DYNAMIC);
break;
}
}
}
}
void
pfi_newgroup(const char *name, int flags)
{
struct pfi_kif *p;
p = pfi_lookup_if(name);
if (p == NULL)
p = pfi_if_create(name, pfi_self, PFI_IFLAG_GROUP);
if (p == NULL) {
printf("pfi_newgroup: cannot allocate '%s' group", name);
return;
}
p->pfik_flags |= flags;
}
void
pfi_fill_oldstatus(struct pf_status *pfs)
{
struct pfi_kif *p, key;
int i, j, k, s;
strlcpy(key.pfik_name, pfs->ifname, sizeof(key.pfik_name));
s = splsoftnet();
p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
if (p == NULL) {
splx(s);
return;
}
bzero(pfs->pcounters, sizeof(pfs->pcounters));
bzero(pfs->bcounters, sizeof(pfs->bcounters));
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
for (k = 0; k < 2; k++) {
pfs->pcounters[i][j][k] =
p->pfik_packets[i][j][k];
pfs->bcounters[i][j] +=
p->pfik_bytes[i][j][k];
}
splx(s);
}
int
pfi_clr_istats(const char *name, int *nzero, int flags)
{
struct pfi_kif *p;
int n = 0, s;
long tzero = time.tv_sec;
s = splsoftnet();
ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE);
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
if (pfi_skip_if(name, p, flags))
continue;
bzero(p->pfik_packets, sizeof(p->pfik_packets));
bzero(p->pfik_bytes, sizeof(p->pfik_bytes));
p->pfik_tzero = tzero;
n++;
}
splx(s);
if (nzero != NULL)
*nzero = n;
return (0);
}
int
pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags)
{
struct pfi_kif *p;
int s, n = 0;
ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE);
s = splsoftnet();
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
if (pfi_skip_if(name, p, flags))
continue;
if (*size > n++) {
if (!p->pfik_tzero)
p->pfik_tzero = boottime.tv_sec;
if (copyout(p, buf++, sizeof(*buf))) {
splx(s);
return (EFAULT);
}
}
}
splx(s);
*size = n;
return (0);
}
struct pfi_kif *
pfi_lookup_if(const char *name)
{
struct pfi_kif *p, key;
strlcpy(key.pfik_name, name, sizeof(key.pfik_name));
p = RB_FIND(pfi_ifhead, &pfi_ifs, &key);
return (p);
}
int
pfi_skip_if(const char *filter, struct pfi_kif *p, int f)
{
int n;
if ((p->pfik_flags & PFI_IFLAG_GROUP) && !(f & PFI_FLAG_GROUP))
return (1);
if ((p->pfik_flags & PFI_IFLAG_INSTANCE) && !(f & PFI_FLAG_INSTANCE))
return (1);
if (filter == NULL || !*filter)
return (0);
if (!strcmp(p->pfik_name, filter))
return (0); /* exact match */
n = strlen(filter);
if (n < 1 || n >= IFNAMSIZ)
return (1); /* sanity check */
if (filter[n-1] >= '0' && filter[n-1] <= '9')
return (1); /* only do exact match in that case */
if (strncmp(p->pfik_name, filter, n))
return (1); /* prefix doesn't match */
return (p->pfik_name[n] < '0' || p->pfik_name[n] > '9');
}
/* from pf_print_state.c */
int
pfi_unmask(void *addr)
{
struct pf_addr *m = addr;
int i = 31, j = 0, b = 0;
u_int32_t tmp;
while (j < 4 && m->addr32[j] == 0xffffffff) {
b += 32;
j++;
}
if (j < 4) {
tmp = ntohl(m->addr32[j]);
for (i = 31; tmp & (1 << i); --i)
b++;
}
return (b);
}
void
pfi_dohooks(struct pfi_kif *p)
{
for (; p != NULL; p = p->pfik_parent)
dohooks(p->pfik_ah_head, 0);
}
int
pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af)
{
if (af == AF_INET) {
switch (dyn->pfid_acnt4) {
case 0:
return (0);
case 1:
return (PF_MATCHA(0, &dyn->pfid_addr4,
&dyn->pfid_mask4, a, AF_INET));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET));
}
} else {
switch (dyn->pfid_acnt6) {
case 0:
return (0);
case 1:
return (PF_MATCHA(0, &dyn->pfid_addr6,
&dyn->pfid_mask6, a, AF_INET6));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6));
}
}
}

2655
sys/dist/pf/net/pf_ioctl.c vendored Normal file

File diff suppressed because it is too large Load Diff

1537
sys/dist/pf/net/pf_norm.c vendored Normal file

File diff suppressed because it is too large Load Diff

525
sys/dist/pf/net/pf_osfp.c vendored Normal file
View File

@ -0,0 +1,525 @@
/* $OpenBSD: pf_osfp.c,v 1.9 2004/01/04 20:08:42 pvalchev Exp $ */
/*
* Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <sys/param.h>
#include <sys/socket.h>
#ifdef _KERNEL
# include <sys/systm.h>
#endif /* _KERNEL */
#include <sys/mbuf.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <net/pfvar.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif /* INET6 */
#ifdef _KERNEL
# define DPFPRINTF(format, x...) \
if (pf_status.debug >= PF_DEBUG_NOISY) \
printf(format , ##x)
typedef struct pool pool_t;
#else
/* Userland equivalents so we can lend code to tcpdump et al. */
# include <arpa/inet.h>
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define pool_t int
# define pool_get(pool, flags) malloc(*(pool))
# define pool_put(pool, item) free(item)
# define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size)
# ifdef PFDEBUG
# include <sys/stdarg.h>
# define DPFPRINTF(format, x...) fprintf(stderr, format , ##x)
# else
# define DPFPRINTF(format, x...) ((void)0)
# endif /* PFDEBUG */
#endif /* _KERNEL */
SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
pool_t pf_osfp_entry_pl;
pool_t pf_osfp_pl;
struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *,
struct pf_os_fingerprint *, u_int8_t);
struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *,
struct pf_os_fingerprint *);
void pf_osfp_insert(struct pf_osfp_list *,
struct pf_os_fingerprint *);
#ifdef _KERNEL
/*
* Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
* Returns the list of possible OSes.
*/
struct pf_osfp_enlist *
pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off,
const struct tcphdr *tcp)
{
struct ip *ip;
char hdr[60];
/* XXX don't have a fingerprint database for IPv6 :-( */
if (pd->af != PF_INET || pd->proto != IPPROTO_TCP || (tcp->th_off << 2)
< sizeof(*tcp))
return (NULL);
ip = mtod(m, struct ip *);
if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL, pd->af))
return (NULL);
return (pf_osfp_fingerprint_hdr(ip, (struct tcphdr *)hdr));
}
#endif /* _KERNEL */
struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct ip *ip, const struct tcphdr *tcp)
{
struct pf_os_fingerprint fp, *fpresult;
int cnt, optlen = 0;
const u_int8_t *optp;
if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN || (ip->ip_off &
htons(IP_OFFMASK)))
return (NULL);
memset(&fp, 0, sizeof(fp));
fp.fp_psize = ntohs(ip->ip_len);
fp.fp_ttl = ip->ip_ttl;
if (ip->ip_off & htons(IP_DF))
fp.fp_flags |= PF_OSFP_DF;
fp.fp_wsize = ntohs(tcp->th_win);
cnt = (tcp->th_off << 2) - sizeof(*tcp);
optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
for (; cnt > 0; cnt -= optlen, optp += optlen) {
if (*optp == TCPOPT_EOL)
break;
fp.fp_optcnt++;
if (*optp == TCPOPT_NOP) {
fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_NOP;
optlen = 1;
} else {
if (cnt < 2)
return (NULL);
optlen = optp[1];
if (optlen > cnt || optlen < 2)
return (NULL);
switch (*optp) {
case TCPOPT_MAXSEG:
if (optlen >= TCPOLEN_MAXSEG)
memcpy(&fp.fp_mss, &optp[2],
sizeof(fp.fp_mss));
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
NTOHS(fp.fp_mss);
break;
case TCPOPT_WINDOW:
if (optlen >= TCPOLEN_WINDOW)
memcpy(&fp.fp_wscale, &optp[2],
sizeof(fp.fp_wscale));
NTOHS(fp.fp_wscale);
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_WSCALE;
break;
case TCPOPT_SACK_PERMITTED:
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
break;
case TCPOPT_TIMESTAMP:
if (optlen >= TCPOLEN_TIMESTAMP) {
u_int32_t ts;
memcpy(&ts, &optp[2], sizeof(ts));
if (ts == 0)
fp.fp_flags |= PF_OSFP_TS0;
}
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
break;
default:
return (NULL);
}
}
optlen = MAX(optlen, 1); /* paranoia */
}
DPFPRINTF("fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) "
"(TS=%s,M=%s%d,W=%s%d)\n",
inet_ntoa(ip->ip_src), ntohs(tcp->th_sport),
fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
(fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fp.fp_mss,
(fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fp.fp_wscale);
if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
PF_OSFP_MAXTTL_OFFSET)))
return (&fpresult->fp_oses);
return (NULL);
}
/* Match a fingerprint ID against a list of OSes */
int
pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
{
struct pf_osfp_entry *entry;
int os_class, os_version, os_subtype;
int en_class, en_version, en_subtype;
if (os == PF_OSFP_ANY)
return (1);
if (list == NULL) {
DPFPRINTF("osfp no match against %x\n", os);
return (os == PF_OSFP_UNKNOWN);
}
PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
SLIST_FOREACH(entry, list, fp_entry) {
PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
(os_version == PF_OSFP_ANY || en_version == os_version) &&
(os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
DPFPRINTF("osfp matched %s %s %s %x==%x\n",
entry->fp_class_nm, entry->fp_version_nm,
entry->fp_subtype_nm, os, entry->fp_os);
return (1);
}
}
DPFPRINTF("fingerprint 0x%x didn't match\n", os);
return (0);
}
/* Initialize the OS fingerprint system */
void
pf_osfp_initialize(void)
{
pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
"pfosfpen", NULL);
pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
"pfosfp", NULL);
SLIST_INIT(&pf_osfp_list);
}
/* Flush the fingerprint list */
void
pf_osfp_flush(void)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
while ((fp = SLIST_FIRST(&pf_osfp_list))) {
SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
while ((entry = SLIST_FIRST(&fp->fp_oses))) {
SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
pool_put(&pf_osfp_entry_pl, entry);
}
pool_put(&pf_osfp_pl, fp);
}
}
/* Add a fingerprint */
int
pf_osfp_add(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp, fpadd;
struct pf_osfp_entry *entry;
memset(&fpadd, 0, sizeof(fpadd));
fpadd.fp_tcpopts = fpioc->fp_tcpopts;
fpadd.fp_wsize = fpioc->fp_wsize;
fpadd.fp_psize = fpioc->fp_psize;
fpadd.fp_mss = fpioc->fp_mss;
fpadd.fp_flags = fpioc->fp_flags;
fpadd.fp_optcnt = fpioc->fp_optcnt;
fpadd.fp_wscale = fpioc->fp_wscale;
fpadd.fp_ttl = fpioc->fp_ttl;
DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
"(TS=%s,M=%s%d,W=%s%d) %x\n",
fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
fpioc->fp_os.fp_subtype_nm,
(fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
(fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
fpadd.fp_wsize,
fpadd.fp_ttl,
(fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
(fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
fpadd.fp_psize,
(long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
(fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fpadd.fp_mss,
(fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fpadd.fp_wscale,
fpioc->fp_os.fp_os);
if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
return (EEXIST);
}
if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL)
return (ENOMEM);
} else {
if ((fp = pool_get(&pf_osfp_pl, PR_NOWAIT)) == NULL)
return (ENOMEM);
memset(fp, 0, sizeof(*fp));
fp->fp_tcpopts = fpioc->fp_tcpopts;
fp->fp_wsize = fpioc->fp_wsize;
fp->fp_psize = fpioc->fp_psize;
fp->fp_mss = fpioc->fp_mss;
fp->fp_flags = fpioc->fp_flags;
fp->fp_optcnt = fpioc->fp_optcnt;
fp->fp_wscale = fpioc->fp_wscale;
fp->fp_ttl = fpioc->fp_ttl;
SLIST_INIT(&fp->fp_oses);
if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL) {
pool_put(&pf_osfp_pl, fp);
return (ENOMEM);
}
pf_osfp_insert(&pf_osfp_list, fp);
}
memcpy(entry, &fpioc->fp_os, sizeof(*entry));
/* Make sure the strings are NUL terminated */
entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
#ifdef PFDEBUG
if ((fp = pf_osfp_validate()))
printf("Invalid fingerprint list\n");
#endif /* PFDEBUG */
return (0);
}
/* Find a fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
u_int8_t ttldiff)
{
struct pf_os_fingerprint *f;
#define MATCH_INT(_MOD, _DC, _field) \
if ((f->fp_flags & _DC) == 0) { \
if ((f->fp_flags & _MOD) == 0) { \
if (f->_field != find->_field) \
continue; \
} else { \
if (f->_field == 0 || find->_field % f->_field) \
continue; \
} \
}
SLIST_FOREACH(f, list, fp_next) {
if (f->fp_tcpopts != find->fp_tcpopts ||
f->fp_optcnt != find->fp_optcnt ||
f->fp_ttl < find->fp_ttl ||
f->fp_ttl - find->fp_ttl > ttldiff ||
(f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
(find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
continue;
MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
if (find->fp_mss == 0)
continue;
/* Some "smart" NAT devices and DSL routers will tweak the MSS size and
* will set it to whatever is suitable for the link type.
*/
#define SMART_MSS 1460
if ((find->fp_wsize % find->fp_mss ||
find->fp_wsize / find->fp_mss !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MSS ||
find->fp_wsize / SMART_MSS !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
if (find->fp_mss == 0)
continue;
#define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr))
#define SMART_MTU (SMART_MSS + MTUOFF)
if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
find->fp_wsize / (find->fp_mss + MTUOFF) !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MTU ||
find->fp_wsize / SMART_MTU !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
if (f->fp_wsize == 0 || find->fp_wsize %
f->fp_wsize)
continue;
} else {
if (f->fp_wsize != find->fp_wsize)
continue;
}
}
return (f);
}
return (NULL);
}
/* Find an exact fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
{
struct pf_os_fingerprint *f;
SLIST_FOREACH(f, list, fp_next) {
if (f->fp_tcpopts == find->fp_tcpopts &&
f->fp_wsize == find->fp_wsize &&
f->fp_psize == find->fp_psize &&
f->fp_mss == find->fp_mss &&
f->fp_flags == find->fp_flags &&
f->fp_optcnt == find->fp_optcnt &&
f->fp_wscale == find->fp_wscale &&
f->fp_ttl == find->fp_ttl)
return (f);
}
return (NULL);
}
/* Insert a fingerprint into the list */
void
pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
{
struct pf_os_fingerprint *f, *prev = NULL;
/* XXX need to go semi tree based. can key on tcp options */
SLIST_FOREACH(f, list, fp_next)
prev = f;
if (prev)
SLIST_INSERT_AFTER(prev, ins, fp_next);
else
SLIST_INSERT_HEAD(list, ins, fp_next);
}
/* Fill a fingerprint by its number (from an ioctl) */
int
pf_osfp_get(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
int num = fpioc->fp_getnum;
int i = 0;
memset(fpioc, 0, sizeof(*fpioc));
SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
if (i++ == num) {
fpioc->fp_mss = fp->fp_mss;
fpioc->fp_wsize = fp->fp_wsize;
fpioc->fp_flags = fp->fp_flags;
fpioc->fp_psize = fp->fp_psize;
fpioc->fp_ttl = fp->fp_ttl;
fpioc->fp_wscale = fp->fp_wscale;
fpioc->fp_getnum = num;
memcpy(&fpioc->fp_os, entry,
sizeof(fpioc->fp_os));
return (0);
}
}
}
return (EBUSY);
}
/* Validate that each signature is reachable */
struct pf_os_fingerprint *
pf_osfp_validate(void)
{
struct pf_os_fingerprint *f, *f2, find;
SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
memcpy(&find, f, sizeof(find));
/* We do a few MSS/th_win percolations to make things unique */
if (find.fp_mss == 0)
find.fp_mss = 128;
if (f->fp_flags & PF_OSFP_WSIZE_MSS)
find.fp_wsize *= find.fp_mss, 1;
else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
find.fp_wsize *= (find.fp_mss + 40);
else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
find.fp_wsize *= 2;
if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
if (f2)
printf("Found \"%s %s %s\" instead of "
"\"%s %s %s\"\n",
SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
else
printf("Couldn't find \"%s %s %s\"\n",
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
return (f);
}
}
return (NULL);
}

2112
sys/dist/pf/net/pf_table.c vendored Normal file

File diff suppressed because it is too large Load Diff

1471
sys/dist/pf/net/pfvar.h vendored Normal file

File diff suppressed because it is too large Load Diff