summaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorMike Frantzen <frantzen@cvs.openbsd.org>2003-08-21 19:12:10 +0000
committerMike Frantzen <frantzen@cvs.openbsd.org>2003-08-21 19:12:10 +0000
commitb52022c22d0099a7ee4fac807fbc3cf0d1ed41dd (patch)
treeee69abaecaf37fc21178586105aec99dbe4500db /sys/net
parentac8ea66182cc0e72f3c2b0178333b53707008bbc (diff)
Add Michal Zalewski's p0f v2 style passive OS fingerprinting to PF.
Exposes the source IP's operating system to the filter language. Interesting policy decisions are now enforceable: . block proto tcp from any os SCO . block proto tcp from any os Windows to any port smtp . rdr ... from any os "Windows 98" to port WWW -> 127.0.0.1 port 8001
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/pf.c118
-rw-r--r--sys/net/pf_ioctl.c27
-rw-r--r--sys/net/pf_norm.c6
-rw-r--r--sys/net/pf_osfp.c507
-rw-r--r--sys/net/pfvar.h127
5 files changed, 731 insertions, 54 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 1a06af8df12..593ed77af13 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.385 2003/08/18 11:01:41 dhartmei Exp $ */
+/* $OpenBSD: pf.c,v 1.386 2003/08/21 19:12:07 frantzen Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -134,13 +134,15 @@ void pf_send_tcp(const struct pf_rule *, sa_family_t,
u_int8_t, u_int16_t, u_int16_t, u_int8_t);
void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t,
sa_family_t, struct pf_rule *);
-struct pf_rule *pf_match_translation(int, struct ifnet *, u_int8_t,
+struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *,
+ int, int, struct ifnet *,
struct pf_addr *, u_int16_t, struct pf_addr *,
- u_int16_t, sa_family_t, int);
-struct pf_rule *pf_get_translation(int, struct ifnet *, u_int8_t,
+ u_int16_t, int);
+struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *,
+ int, int, struct ifnet *,
struct pf_addr *, u_int16_t,
struct pf_addr *, u_int16_t,
- struct pf_addr *, u_int16_t *, sa_family_t);
+ struct pf_addr *, u_int16_t *);
int pf_test_tcp(struct pf_rule **, struct pf_state **,
int, struct ifnet *, struct mbuf *, int, int,
void *, struct pf_pdesc *, struct pf_rule **,
@@ -154,7 +156,7 @@ int pf_test_icmp(struct pf_rule **, struct pf_state **,
void *, struct pf_pdesc *, struct pf_rule **,
struct pf_ruleset **);
int pf_test_other(struct pf_rule **, struct pf_state **,
- int, struct ifnet *, struct mbuf *, void *,
+ int, struct ifnet *, struct mbuf *, int, void *,
struct pf_pdesc *, struct pf_rule **,
struct pf_ruleset **);
int pf_test_fragment(struct pf_rule **, int,
@@ -1761,9 +1763,9 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool,
}
struct pf_rule *
-pf_match_translation(int direction, struct ifnet *ifp, u_int8_t proto,
- struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr,
- u_int16_t dport, sa_family_t af, int rs_num)
+pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off,
+ int direction, struct ifnet *ifp, struct pf_addr *saddr, u_int16_t sport,
+ struct pf_addr *daddr, u_int16_t dport, int rs_num)
{
struct pf_rule *r, *rm = NULL, *anchorrule = NULL;
struct pf_ruleset *ruleset = NULL;
@@ -1788,11 +1790,11 @@ pf_match_translation(int direction, struct ifnet *ifp, u_int8_t proto,
r = r->skip[PF_SKIP_IFP].ptr;
else if (r->direction && r->direction != direction)
r = r->skip[PF_SKIP_DIR].ptr;
- else if (r->af && r->af != af)
+ else if (r->af && r->af != pd->af)
r = r->skip[PF_SKIP_AF].ptr;
- else if (r->proto && r->proto != proto)
+ else if (r->proto && r->proto != pd->proto)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&src->addr, saddr, af, src->not))
+ else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, src->not))
r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR :
PF_SKIP_DST_ADDR].ptr;
else if (src->port_op && !pf_match_port(src->port_op,
@@ -1800,14 +1802,18 @@ pf_match_translation(int direction, struct ifnet *ifp, u_int8_t proto,
r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT :
PF_SKIP_DST_PORT].ptr;
else if (dst != NULL &&
- PF_MISMATCHAW(&dst->addr, daddr, af, dst->not))
+ PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->not))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
- else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, af, 0))
+ else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, 0))
r = TAILQ_NEXT(r, entries);
else if (dst != NULL && dst->port_op &&
!pf_match_port(dst->port_op, dst->port[0],
dst->port[1], dport))
r = r->skip[PF_SKIP_DST_PORT].ptr;
+ else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto !=
+ IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m,
+ off, pd->hdr.tcp), r->os_fingerprint)))
+ r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
else if (r->anchor == NULL)
@@ -1825,25 +1831,26 @@ pf_match_translation(int direction, struct ifnet *ifp, u_int8_t proto,
}
struct pf_rule *
-pf_get_translation(int direction, struct ifnet *ifp, u_int8_t proto,
+pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
+ struct ifnet *ifp,
struct pf_addr *saddr, u_int16_t sport,
struct pf_addr *daddr, u_int16_t dport,
- struct pf_addr *naddr, u_int16_t *nport, sa_family_t af)
+ struct pf_addr *naddr, u_int16_t *nport)
{
struct pf_rule *r = NULL;
if (direction == PF_OUT) {
- r = pf_match_translation(direction, ifp, proto,
- saddr, sport, daddr, dport, af, PF_RULESET_BINAT);
+ r = pf_match_translation(pd, m, off, direction, ifp, saddr,
+ sport, daddr, dport, PF_RULESET_BINAT);
if (r == NULL)
- r = pf_match_translation(direction, ifp, proto,
- saddr, sport, daddr, dport, af, PF_RULESET_NAT);
+ r = pf_match_translation(pd, m, off, direction, ifp,
+ saddr, sport, daddr, dport, PF_RULESET_NAT);
} else {
- r = pf_match_translation(direction, ifp, proto,
- saddr, sport, daddr, dport, af, PF_RULESET_RDR);
+ r = pf_match_translation(pd, m, off, direction, ifp, saddr,
+ sport, daddr, dport, PF_RULESET_RDR);
if (r == NULL)
- r = pf_match_translation(direction, ifp, proto,
- saddr, sport, daddr, dport, af, PF_RULESET_BINAT);
+ r = pf_match_translation(pd, m, off, direction, ifp,
+ saddr, sport, daddr, dport, PF_RULESET_BINAT);
}
if (r != NULL) {
@@ -1854,8 +1861,8 @@ pf_get_translation(int direction, struct ifnet *ifp, u_int8_t proto,
return (NULL);
break;
case PF_NAT:
- if (pf_get_sport(af, proto, &r->rpool, saddr, daddr,
- dport, naddr, nport, r->rpool.proxy_port[0],
+ if (pf_get_sport(pd->af, pd->proto, &r->rpool, saddr,
+ daddr, dport, naddr, nport, r->rpool.proxy_port[0],
r->rpool.proxy_port[1])) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: NAT proxy port allocation "
@@ -1876,7 +1883,7 @@ pf_get_translation(int direction, struct ifnet *ifp, u_int8_t proto,
PF_POOLMASK(naddr,
&r->rpool.cur->addr.v.a.addr,
&r->rpool.cur->addr.v.a.mask,
- saddr, af);
+ saddr, pd->af);
break;
case PF_IN:
if (r->src.addr.type == PF_ADDR_DYNIFTL &&
@@ -1885,7 +1892,8 @@ pf_get_translation(int direction, struct ifnet *ifp, u_int8_t proto,
else
PF_POOLMASK(naddr,
&r->src.addr.v.a.addr,
- &r->src.addr.v.a.mask, saddr, af);
+ &r->src.addr.v.a.mask, saddr,
+ pd->af);
break;
}
break;
@@ -2167,9 +2175,9 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
bport = nport = th->th_sport;
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(PF_OUT, ifp, IPPROTO_TCP,
+ if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp,
saddr, th->th_sport, daddr, th->th_dport,
- &naddr, &nport, af)) != NULL) {
+ &naddr, &nport)) != NULL) {
PF_ACPY(&baddr, saddr, af);
pf_change_ap(saddr, &th->th_sport, pd->ip_sum,
&th->th_sum, &naddr, nport, 0, af);
@@ -2180,9 +2188,9 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
} else {
bport = nport = th->th_dport;
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(PF_IN, ifp, IPPROTO_TCP,
- saddr, th->th_sport, daddr, th->th_dport,
- &naddr, &nport, af)) != NULL) {
+ if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr,
+ th->th_sport, daddr, th->th_dport,
+ &naddr, &nport)) != NULL) {
PF_ACPY(&baddr, daddr, af);
pf_change_ap(daddr, &th->th_dport, pd->ip_sum,
&th->th_sum, &naddr, nport, 0, af);
@@ -2236,6 +2244,9 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
+ else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match(
+ pf_osfp_fingerprint(pd, m, off, th), r->os_fingerprint))
+ r = TAILQ_NEXT(r, entries);
else {
if (r->tag)
tag = r->tag;
@@ -2487,9 +2498,9 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
bport = nport = uh->uh_sport;
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(PF_OUT, ifp, IPPROTO_UDP,
+ if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp,
saddr, uh->uh_sport, daddr, uh->uh_dport,
- &naddr, &nport, af)) != NULL) {
+ &naddr, &nport)) != NULL) {
PF_ACPY(&baddr, saddr, af);
pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum,
&uh->uh_sum, &naddr, nport, 1, af);
@@ -2500,9 +2511,9 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
} else {
bport = nport = uh->uh_dport;
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(PF_IN, ifp, IPPROTO_UDP,
- saddr, uh->uh_sport, daddr, uh->uh_dport,
- &naddr, &nport, af)) != NULL) {
+ if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr,
+ uh->uh_sport, daddr, uh->uh_dport, &naddr, &nport))
+ != NULL) {
PF_ACPY(&baddr, daddr, af);
pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum,
&uh->uh_sum, &naddr, nport, 1, af);
@@ -2554,6 +2565,8 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
+ else if (r->os_fingerprint != PF_OSFP_ANY)
+ r = TAILQ_NEXT(r, entries);
else {
if (r->tag)
tag = r->tag;
@@ -2757,8 +2770,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(PF_OUT, ifp, pd->proto,
- saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) {
+ if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0,
+ daddr, 0, &naddr, NULL)) != NULL) {
PF_ACPY(&baddr, saddr, af);
switch (af) {
#ifdef INET
@@ -2780,8 +2793,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
}
} else {
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(PF_IN, ifp, pd->proto,
- saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) {
+ if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0,
+ daddr, 0, &naddr, NULL)) != NULL) {
PF_ACPY(&baddr, daddr, af);
switch (af) {
#ifdef INET
@@ -2831,6 +2844,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
+ else if (r->os_fingerprint != PF_OSFP_ANY)
+ r = TAILQ_NEXT(r, entries);
else {
if (r->tag)
tag = r->tag;
@@ -2963,7 +2978,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
int
pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
- struct ifnet *ifp, struct mbuf *m, void *h, struct pf_pdesc *pd,
+ struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd,
struct pf_rule **am, struct pf_ruleset **rsm)
{
struct pf_rule *nat = NULL, *rdr = NULL;
@@ -2980,8 +2995,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
if (direction == PF_OUT) {
/* check outgoing packet for BINAT/NAT */
- if ((nat = pf_get_translation(PF_OUT, ifp, pd->proto,
- saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) {
+ if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0,
+ daddr, 0, &naddr, NULL)) != NULL) {
PF_ACPY(&baddr, saddr, af);
switch (af) {
#ifdef INET
@@ -3001,8 +3016,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
}
} else {
/* check incoming packet for BINAT/RDR */
- if ((rdr = pf_get_translation(PF_IN, ifp, pd->proto,
- saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) {
+ if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0,
+ daddr, 0, &naddr, NULL)) != NULL) {
PF_ACPY(&baddr, daddr, af);
switch (af) {
#ifdef INET
@@ -3046,6 +3061,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
r = TAILQ_NEXT(r, entries);
else if (r->anchorname[0] && r->anchor == NULL)
r = TAILQ_NEXT(r, entries);
+ else if (r->os_fingerprint != PF_OSFP_ANY)
+ r = TAILQ_NEXT(r, entries);
else {
if (r->tag)
tag = r->tag;
@@ -3227,7 +3244,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp,
else if (r->tos && !(r->tos & pd->tos))
r = TAILQ_NEXT(r, entries);
else if (r->src.port_op || r->dst.port_op ||
- r->flagset || r->type || r->code)
+ r->flagset || r->type || r->code ||
+ r->os_fingerprint != PF_OSFP_ANY)
r = TAILQ_NEXT(r, entries);
else if (r->match_tag &&
!pf_match_tag(m, r, NULL, NULL, pftag, &tag))
@@ -4974,7 +4992,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0)
a = s->anchor.ptr;
log = s->log;
} else if (s == NULL)
- action = pf_test_other(&r, &s, dir, ifp, m, h,
+ action = pf_test_other(&r, &s, dir, ifp, m, off, h,
&pd, &a, &ruleset);
break;
}
@@ -5208,7 +5226,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0)
}
default:
- action = pf_test_other(&r, &s, dir, ifp, m, h,
+ action = pf_test_other(&r, &s, dir, ifp, m, off, h,
&pd, &a, &ruleset);
break;
}
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index 6becc2d21ac..41d0cf6cb32 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_ioctl.c,v 1.79 2003/08/11 20:15:45 dhartmei Exp $ */
+/* $OpenBSD: pf_ioctl.c,v 1.80 2003/08/21 19:12:08 frantzen Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -107,6 +107,7 @@ pfattach(int num)
pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0,
"pfpooladdrpl", NULL);
pfr_initialize();
+ pf_osfp_initialize();
pool_sethardlimit(&pf_state_pl, pf_pool_limits[PF_LIMIT_STATES].limit,
NULL, 0);
@@ -540,6 +541,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
case DIOCRGETASTATS:
case DIOCRCLRASTATS:
case DIOCRTSTADDRS:
+ case DIOCOSFPGET:
break;
default:
return (EPERM);
@@ -568,6 +570,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
case DIOCRGETADDRS:
case DIOCRGETASTATS:
case DIOCRTSTADDRS:
+ case DIOCOSFPGET:
break;
default:
return (EACCES);
@@ -2123,6 +2126,28 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
break;
}
+ case DIOCOSFPFLUSH:
+ s = splsoftnet();
+ pf_osfp_flush();
+ splx(s);
+ break;
+
+ case DIOCOSFPADD: {
+ struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
+ s = splsoftnet();
+ error = pf_osfp_add(io);
+ splx(s);
+ break;
+ }
+
+ case DIOCOSFPGET: {
+ struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr;
+ s = splsoftnet();
+ error = pf_osfp_get(io);
+ splx(s);
+ break;
+ }
+
default:
error = ENODEV;
break;
diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c
index cc49e4ed1b3..4ef322954ac 100644
--- a/sys/net/pf_norm.c
+++ b/sys/net/pf_norm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_norm.c,v 1.71 2003/08/14 19:00:12 jason Exp $ */
+/* $OpenBSD: pf_norm.c,v 1.72 2003/08/21 19:12:08 frantzen Exp $ */
/*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
@@ -1211,6 +1211,10 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff,
else if (r->dst.port_op && !pf_match_port(r->dst.port_op,
r->dst.port[0], r->dst.port[1], th->th_dport))
r = r->skip[PF_SKIP_DST_PORT].ptr;
+ else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match(
+ pf_osfp_fingerprint(pd, m, off, th),
+ r->os_fingerprint))
+ r = TAILQ_NEXT(r, entries);
else {
rm = r;
break;
diff --git a/sys/net/pf_osfp.c b/sys/net/pf_osfp.c
new file mode 100644
index 00000000000..c72914aaba1
--- /dev/null
+++ b/sys/net/pf_osfp.c
@@ -0,0 +1,507 @@
+/* $OpenBSD: pf_osfp.c,v 1.1 2003/08/21 19:12:08 frantzen 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>
+# 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 <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;
+ 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 = (caddr_t)tcp + sizeof(*tcp);
+ for (; cnt > 0; cnt -= optlen, optp += optlen) {
+ fp.fp_optcnt++;
+ if (*optp == TCPOPT_EOL)
+ break;
+ else 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 ||
+ find->fp_wsize % find->fp_mss ||
+ find->fp_wsize / find->fp_mss !=
+ f->fp_wsize)
+ continue;
+ } else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
+ if (find->fp_mss == 0 ||
+ find->fp_wsize % (find->fp_mss + 40) ||
+ find->fp_wsize / (find->fp_mss + 40) !=
+ 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);
+}
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index c4e2c19d8db..1f577c3b9dc 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.166 2003/08/09 14:56:48 cedric Exp $ */
+/* $OpenBSD: pfvar.h,v 1.167 2003/08/21 19:12:08 frantzen Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -41,6 +41,8 @@
#include <netinet/ip_ipsp.h>
#include <netinet/tcp_fsm.h>
+struct ip;
+
#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0)
#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1)
@@ -51,7 +53,7 @@ enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT,
PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX };
enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT,
PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG };
-enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC };
+enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY };
enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL,
PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER,
PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET };
@@ -322,6 +324,108 @@ struct pf_pool {
u_int8_t opts;
};
+
+/* A packed Operating System description for fingerprinting */
+typedef u_int32_t pf_osfp_t;
+#define PF_OSFP_ANY ((pf_osfp_t)0)
+#define PF_OSFP_UNKNOWN ((pf_osfp_t)-1)
+#define PF_OSFP_NOMATCH ((pf_osfp_t)-2)
+
+struct pf_osfp_entry {
+ SLIST_ENTRY(pf_osfp_entry) fp_entry;
+ pf_osfp_t fp_os;
+ int fp_enflags;
+#define PF_OSFP_EXPANDED 0x001 /* expanded entry */
+#define PF_OSFP_GENERIC 0x002 /* generic signature */
+#define PF_OSFP_NODETAIL 0x004 /* no p0f details */
+#define PF_OSFP_LEN 32
+ char fp_class_nm[PF_OSFP_LEN];
+ char fp_version_nm[PF_OSFP_LEN];
+ char fp_subtype_nm[PF_OSFP_LEN];
+};
+#define PF_OSFP_ENTRY_EQ(a, b) \
+ ((a)->fp_os == (b)->fp_os && \
+ memcmp((a)->fp_class_nm, (b)->fp_class_nm, PF_OSFP_LEN) == 0 && \
+ memcmp((a)->fp_version_nm, (b)->fp_version_nm, PF_OSFP_LEN) == 0 && \
+ memcmp((a)->fp_subtype_nm, (b)->fp_subtype_nm, PF_OSFP_LEN) == 0)
+
+/* handle pf_osfp_t packing */
+#define _FP_RESERVED_BIT 1 /* For the special negative #defines */
+#define _FP_UNUSED_BITS 1
+#define _FP_CLASS_BITS 10 /* OS Class (Windows, Linux) */
+#define _FP_VERSION_BITS 10 /* OS version (95, 98, NT, 2.4.54, 3.2) */
+#define _FP_SUBTYPE_BITS 10 /* patch level (NT SP4, SP3, ECN patch) */
+#define PF_OSFP_UNPACK(osfp, class, version, subtype) do { \
+ (class) = ((osfp) >> (_FP_VERSION_BITS+_FP_SUBTYPE_BITS)) & \
+ ((1 << _FP_CLASS_BITS) - 1); \
+ (version) = ((osfp) >> _FP_SUBTYPE_BITS) & \
+ ((1 << _FP_VERSION_BITS) - 1);\
+ (subtype) = (osfp) & ((1 << _FP_SUBTYPE_BITS) - 1); \
+} while(0)
+#define PF_OSFP_PACK(osfp, class, version, subtype) do { \
+ (osfp) = ((class) & ((1 << _FP_CLASS_BITS) - 1)) << (_FP_VERSION_BITS \
+ + _FP_SUBTYPE_BITS); \
+ (osfp) |= ((version) & ((1 << _FP_VERSION_BITS) - 1)) << \
+ _FP_SUBTYPE_BITS; \
+ (osfp) |= (subtype) & ((1 << _FP_SUBTYPE_BITS) - 1); \
+} while(0)
+
+/* the fingerprint of an OSes TCP SYN packet */
+typedef u_int64_t pf_tcpopts_t;
+struct pf_os_fingerprint {
+ SLIST_HEAD(pf_osfp_enlist, pf_osfp_entry) fp_oses; /* list of matches */
+ pf_tcpopts_t fp_tcpopts; /* packed TCP options */
+ u_int16_t fp_wsize; /* TCP window size */
+ u_int16_t fp_psize; /* ip->ip_len */
+ u_int16_t fp_mss; /* TCP MSS */
+ u_int16_t fp_flags;
+#define PF_OSFP_WSIZE_MOD 0x0001 /* Window modulus */
+#define PF_OSFP_WSIZE_DC 0x0002 /* Window don't care */
+#define PF_OSFP_WSIZE_MSS 0x0004 /* Window multiple of MSS */
+#define PF_OSFP_WSIZE_MTU 0x0008 /* Window multiple of MTU */
+#define PF_OSFP_PSIZE_MOD 0x0010 /* packet size modulus */
+#define PF_OSFP_PSIZE_DC 0x0020 /* packet size don't care */
+#define PF_OSFP_WSCALE 0x0040 /* TCP window scaling */
+#define PF_OSFP_WSCALE_MOD 0x0080 /* TCP window scale modulus */
+#define PF_OSFP_WSCALE_DC 0x0100 /* TCP window scale dont-care */
+#define PF_OSFP_MSS 0x0200 /* TCP MSS */
+#define PF_OSFP_MSS_MOD 0x0400 /* TCP MSS modulus */
+#define PF_OSFP_MSS_DC 0x0800 /* TCP MSS dont-care */
+#define PF_OSFP_DF 0x1000 /* IPv4 don't fragment bit */
+#define PF_OSFP_TS0 0x2000 /* Zero timestamp */
+ u_int8_t fp_optcnt; /* TCP option count */
+ u_int8_t fp_wscale; /* TCP window scaling */
+ u_int8_t fp_ttl; /* IPv4 TTL */
+#define PF_OSFP_MAXTTL_OFFSET 40
+/* TCP options packing */
+#define PF_OSFP_TCPOPT_NOP 0x0 /* TCP NOP option */
+#define PF_OSFP_TCPOPT_WSCALE 0x1 /* TCP window scaling option */
+#define PF_OSFP_TCPOPT_MSS 0x2 /* TCP max segment size opt */
+#define PF_OSFP_TCPOPT_SACK 0x3 /* TCP SACK OK option */
+#define PF_OSFP_TCPOPT_TS 0x4 /* TCP timestamp option */
+#define PF_OSFP_TCPOPT_BITS 3 /* bits used by each option */
+#define PF_OSFP_MAX_OPTS \
+ (sizeof(((struct pf_os_fingerprint *)0)->fp_tcpopts) * 8) \
+ / PF_OSFP_TCPOPT_BITS
+
+ SLIST_ENTRY(pf_os_fingerprint) fp_next;
+};
+
+struct pf_osfp_ioctl {
+ struct pf_osfp_entry fp_os;
+ pf_tcpopts_t fp_tcpopts; /* packed TCP options */
+ u_int16_t fp_wsize; /* TCP window size */
+ u_int16_t fp_psize; /* ip->ip_len */
+ u_int16_t fp_mss; /* TCP MSS */
+ u_int16_t fp_flags;
+ u_int8_t fp_optcnt; /* TCP option count */
+ u_int8_t fp_wscale; /* TCP window scaling */
+ u_int8_t fp_ttl; /* IPv4 TTL */
+
+ int fp_getnum; /* DIOCOSFPGET number */
+};
+
+
union pf_rule_ptr {
struct pf_rule *ptr;
u_int32_t nr;
@@ -363,6 +467,7 @@ struct pf_rule {
struct ifnet *ifp;
struct pf_anchor *anchor;
+ pf_osfp_t os_fingerprint;
u_int32_t states;
u_int32_t max_states;
u_int32_t qid;
@@ -993,6 +1098,9 @@ struct pfioc_table {
#define DIOCRINABEGIN _IOWR('D', 75, struct pfioc_table)
#define DIOCRINACOMMIT _IOWR('D', 76, struct pfioc_table)
#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table)
+#define DIOCOSFPFLUSH _IO('D', 78)
+#define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl)
+#define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl)
#ifdef _KERNEL
RB_HEAD(pf_state_tree, pf_tree_node);
@@ -1136,4 +1244,19 @@ extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX];
#endif /* _KERNEL */
+/* The fingerprint functions can be linked into userland programs (tcpdump) */
+int pf_osfp_add(struct pf_osfp_ioctl *);
+struct pf_osfp_enlist *
+ pf_osfp_fingerprint(struct pf_pdesc *, struct mbuf *, int,
+ const struct tcphdr *);
+struct pf_osfp_enlist *
+ pf_osfp_fingerprint_hdr(const struct ip *, const struct tcphdr *);
+void pf_osfp_flush(void);
+int pf_osfp_get(struct pf_osfp_ioctl *);
+void pf_osfp_initialize(void);
+int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t);
+struct pf_os_fingerprint *
+ pf_osfp_validate(void);
+
+
#endif /* _NET_PFVAR_H_ */