diff options
author | Mike Frantzen <frantzen@cvs.openbsd.org> | 2003-08-21 19:12:10 +0000 |
---|---|---|
committer | Mike Frantzen <frantzen@cvs.openbsd.org> | 2003-08-21 19:12:10 +0000 |
commit | b52022c22d0099a7ee4fac807fbc3cf0d1ed41dd (patch) | |
tree | ee69abaecaf37fc21178586105aec99dbe4500db /sys/net | |
parent | ac8ea66182cc0e72f3c2b0178333b53707008bbc (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.c | 118 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 27 | ||||
-rw-r--r-- | sys/net/pf_norm.c | 6 | ||||
-rw-r--r-- | sys/net/pf_osfp.c | 507 | ||||
-rw-r--r-- | sys/net/pfvar.h | 127 |
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_ */ |