1d8aa10ccSGleb Smirnoff /*-
26e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
36e778a7eSPedro F. Giffuni *
43b3a8eb9SGleb Smirnoff * Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
53b3a8eb9SGleb Smirnoff *
63b3a8eb9SGleb Smirnoff * Permission to use, copy, modify, and distribute this software for any
73b3a8eb9SGleb Smirnoff * purpose with or without fee is hereby granted, provided that the above
83b3a8eb9SGleb Smirnoff * copyright notice and this permission notice appear in all copies.
93b3a8eb9SGleb Smirnoff *
103b3a8eb9SGleb Smirnoff * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113b3a8eb9SGleb Smirnoff * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123b3a8eb9SGleb Smirnoff * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133b3a8eb9SGleb Smirnoff * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143b3a8eb9SGleb Smirnoff * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153b3a8eb9SGleb Smirnoff * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163b3a8eb9SGleb Smirnoff * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173b3a8eb9SGleb Smirnoff *
18d8aa10ccSGleb Smirnoff * $OpenBSD: pf_osfp.c,v 1.14 2008/06/12 18:17:01 henning Exp $
193b3a8eb9SGleb Smirnoff */
203b3a8eb9SGleb Smirnoff
213b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
22643faabeSEric van Gyzen #include "opt_inet6.h"
23643faabeSEric van Gyzen
243b3a8eb9SGleb Smirnoff #include <sys/param.h>
253b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
2676039bc8SGleb Smirnoff #include <sys/lock.h>
2776039bc8SGleb Smirnoff #include <sys/mbuf.h>
283b3a8eb9SGleb Smirnoff #include <sys/socket.h>
293b3a8eb9SGleb Smirnoff
303b3a8eb9SGleb Smirnoff #include <netinet/in.h>
313b3a8eb9SGleb Smirnoff #include <netinet/ip.h>
323b3a8eb9SGleb Smirnoff #include <netinet/tcp.h>
333b3a8eb9SGleb Smirnoff
343b3a8eb9SGleb Smirnoff #include <net/if.h>
3576039bc8SGleb Smirnoff #include <net/vnet.h>
363b3a8eb9SGleb Smirnoff #include <net/pfvar.h>
373b3a8eb9SGleb Smirnoff
38643faabeSEric van Gyzen #ifdef INET6
393b3a8eb9SGleb Smirnoff #include <netinet/ip6.h>
40643faabeSEric van Gyzen #endif
413b3a8eb9SGleb Smirnoff
423b3a8eb9SGleb Smirnoff static MALLOC_DEFINE(M_PFOSFP, "pf_osfp", "pf(4) operating system fingerprints");
433b3a8eb9SGleb Smirnoff #define DPFPRINTF(format, x...) \
443b3a8eb9SGleb Smirnoff if (V_pf_status.debug >= PF_DEBUG_NOISY) \
453b3a8eb9SGleb Smirnoff printf(format , ##x)
463b3a8eb9SGleb Smirnoff
473b3a8eb9SGleb Smirnoff SLIST_HEAD(pf_osfp_list, pf_os_fingerprint);
485f901c92SAndrew Turner VNET_DEFINE_STATIC(struct pf_osfp_list, pf_osfp_list) =
493b3a8eb9SGleb Smirnoff SLIST_HEAD_INITIALIZER();
503b3a8eb9SGleb Smirnoff #define V_pf_osfp_list VNET(pf_osfp_list)
513b3a8eb9SGleb Smirnoff
523b3a8eb9SGleb Smirnoff static struct pf_osfp_enlist *pf_osfp_fingerprint_hdr(const struct ip *,
533b3a8eb9SGleb Smirnoff const struct ip6_hdr *,
543b3a8eb9SGleb Smirnoff const struct tcphdr *);
553b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *,
563b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *, u_int8_t);
573b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *,
583b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *);
593b3a8eb9SGleb Smirnoff static void pf_osfp_insert(struct pf_osfp_list *,
603b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *);
613b3a8eb9SGleb Smirnoff #ifdef PFDEBUG
623b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *pf_osfp_validate(void);
633b3a8eb9SGleb Smirnoff #endif
643b3a8eb9SGleb Smirnoff
653b3a8eb9SGleb Smirnoff /*
663b3a8eb9SGleb Smirnoff * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
673b3a8eb9SGleb Smirnoff * Returns the list of possible OSes.
683b3a8eb9SGleb Smirnoff */
693b3a8eb9SGleb Smirnoff struct pf_osfp_enlist *
pf_osfp_fingerprint(struct pf_pdesc * pd,const struct tcphdr * tcp)709a405864SKristof Provost pf_osfp_fingerprint(struct pf_pdesc *pd, const struct tcphdr *tcp)
713b3a8eb9SGleb Smirnoff {
7208b53c6eSKristof Provost struct ip *ip = NULL;
7308b53c6eSKristof Provost struct ip6_hdr *ip6 = NULL;
743b3a8eb9SGleb Smirnoff char hdr[60];
753b3a8eb9SGleb Smirnoff
7608b53c6eSKristof Provost if (pd->proto != IPPROTO_TCP || (tcp->th_off << 2) < sizeof(*tcp))
773b3a8eb9SGleb Smirnoff return (NULL);
783b3a8eb9SGleb Smirnoff
7908b53c6eSKristof Provost switch (pd->af) {
8008b53c6eSKristof Provost case AF_INET:
819a405864SKristof Provost ip = mtod(pd->m, struct ip *);
823b3a8eb9SGleb Smirnoff ip6 = (struct ip6_hdr *)NULL;
8308b53c6eSKristof Provost break;
8408b53c6eSKristof Provost case AF_INET6:
859a405864SKristof Provost ip6 = mtod(pd->m, struct ip6_hdr *);
8608b53c6eSKristof Provost break;
873b3a8eb9SGleb Smirnoff }
889a405864SKristof Provost if (!pf_pull_hdr(pd->m, pd->off, hdr, tcp->th_off << 2, NULL, NULL,
893b3a8eb9SGleb Smirnoff pd->af)) return (NULL);
903b3a8eb9SGleb Smirnoff
913b3a8eb9SGleb Smirnoff return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr));
923b3a8eb9SGleb Smirnoff }
933b3a8eb9SGleb Smirnoff
943b3a8eb9SGleb Smirnoff static struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct ip * ip,const struct ip6_hdr * ip6,const struct tcphdr * tcp)953b3a8eb9SGleb Smirnoff pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp)
963b3a8eb9SGleb Smirnoff {
973b3a8eb9SGleb Smirnoff struct pf_os_fingerprint fp, *fpresult;
983b3a8eb9SGleb Smirnoff int cnt, optlen = 0;
993b3a8eb9SGleb Smirnoff const u_int8_t *optp;
100643faabeSEric van Gyzen #ifdef INET6
101643faabeSEric van Gyzen char srcname[INET6_ADDRSTRLEN];
102643faabeSEric van Gyzen #else
103643faabeSEric van Gyzen char srcname[INET_ADDRSTRLEN];
104643faabeSEric van Gyzen #endif
1053b3a8eb9SGleb Smirnoff
1060fc7bdc9SRichard Scheffenegger if ((tcp_get_flags(tcp) & (TH_SYN|TH_ACK)) != TH_SYN)
1073b3a8eb9SGleb Smirnoff return (NULL);
1083b3a8eb9SGleb Smirnoff if (ip) {
1093b3a8eb9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) != 0)
1103b3a8eb9SGleb Smirnoff return (NULL);
1113b3a8eb9SGleb Smirnoff }
1123b3a8eb9SGleb Smirnoff
1133b3a8eb9SGleb Smirnoff memset(&fp, 0, sizeof(fp));
1143b3a8eb9SGleb Smirnoff
1153b3a8eb9SGleb Smirnoff if (ip) {
1163b3a8eb9SGleb Smirnoff fp.fp_psize = ntohs(ip->ip_len);
1173b3a8eb9SGleb Smirnoff fp.fp_ttl = ip->ip_ttl;
1183b3a8eb9SGleb Smirnoff if (ip->ip_off & htons(IP_DF))
1193b3a8eb9SGleb Smirnoff fp.fp_flags |= PF_OSFP_DF;
120643faabeSEric van Gyzen inet_ntoa_r(ip->ip_src, srcname);
1213b3a8eb9SGleb Smirnoff }
1223b3a8eb9SGleb Smirnoff #ifdef INET6
1233b3a8eb9SGleb Smirnoff else if (ip6) {
1243b3a8eb9SGleb Smirnoff /* jumbo payload? */
1253b3a8eb9SGleb Smirnoff fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
1263b3a8eb9SGleb Smirnoff fp.fp_ttl = ip6->ip6_hlim;
1273b3a8eb9SGleb Smirnoff fp.fp_flags |= PF_OSFP_DF;
1283b3a8eb9SGleb Smirnoff fp.fp_flags |= PF_OSFP_INET6;
129643faabeSEric van Gyzen ip6_sprintf(srcname, (const struct in6_addr *)&ip6->ip6_src);
1303b3a8eb9SGleb Smirnoff }
1313b3a8eb9SGleb Smirnoff #endif
1323b3a8eb9SGleb Smirnoff else
1333b3a8eb9SGleb Smirnoff return (NULL);
1343b3a8eb9SGleb Smirnoff fp.fp_wsize = ntohs(tcp->th_win);
1353b3a8eb9SGleb Smirnoff
1363b3a8eb9SGleb Smirnoff cnt = (tcp->th_off << 2) - sizeof(*tcp);
1373b3a8eb9SGleb Smirnoff optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
1383b3a8eb9SGleb Smirnoff for (; cnt > 0; cnt -= optlen, optp += optlen) {
1393b3a8eb9SGleb Smirnoff if (*optp == TCPOPT_EOL)
1403b3a8eb9SGleb Smirnoff break;
1413b3a8eb9SGleb Smirnoff
1423b3a8eb9SGleb Smirnoff fp.fp_optcnt++;
1433b3a8eb9SGleb Smirnoff if (*optp == TCPOPT_NOP) {
1443b3a8eb9SGleb Smirnoff fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
1453b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_NOP;
1463b3a8eb9SGleb Smirnoff optlen = 1;
1473b3a8eb9SGleb Smirnoff } else {
1483b3a8eb9SGleb Smirnoff if (cnt < 2)
1493b3a8eb9SGleb Smirnoff return (NULL);
1503b3a8eb9SGleb Smirnoff optlen = optp[1];
1513b3a8eb9SGleb Smirnoff if (optlen > cnt || optlen < 2)
1523b3a8eb9SGleb Smirnoff return (NULL);
1533b3a8eb9SGleb Smirnoff switch (*optp) {
1543b3a8eb9SGleb Smirnoff case TCPOPT_MAXSEG:
1553b3a8eb9SGleb Smirnoff if (optlen >= TCPOLEN_MAXSEG)
1563b3a8eb9SGleb Smirnoff memcpy(&fp.fp_mss, &optp[2],
1573b3a8eb9SGleb Smirnoff sizeof(fp.fp_mss));
1583b3a8eb9SGleb Smirnoff fp.fp_tcpopts = (fp.fp_tcpopts <<
1593b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
160*4cdcdf0eSKristof Provost fp.fp_mss = ntohs(fp.fp_mss);
1613b3a8eb9SGleb Smirnoff break;
1623b3a8eb9SGleb Smirnoff case TCPOPT_WINDOW:
1633b3a8eb9SGleb Smirnoff if (optlen >= TCPOLEN_WINDOW)
1643b3a8eb9SGleb Smirnoff memcpy(&fp.fp_wscale, &optp[2],
1653b3a8eb9SGleb Smirnoff sizeof(fp.fp_wscale));
166*4cdcdf0eSKristof Provost fp.fp_wscale = ntohs(fp.fp_wscale);
1673b3a8eb9SGleb Smirnoff fp.fp_tcpopts = (fp.fp_tcpopts <<
1683b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_BITS) |
1693b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_WSCALE;
1703b3a8eb9SGleb Smirnoff break;
1713b3a8eb9SGleb Smirnoff case TCPOPT_SACK_PERMITTED:
1723b3a8eb9SGleb Smirnoff fp.fp_tcpopts = (fp.fp_tcpopts <<
1733b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
1743b3a8eb9SGleb Smirnoff break;
1753b3a8eb9SGleb Smirnoff case TCPOPT_TIMESTAMP:
1763b3a8eb9SGleb Smirnoff if (optlen >= TCPOLEN_TIMESTAMP) {
1773b3a8eb9SGleb Smirnoff u_int32_t ts;
1783b3a8eb9SGleb Smirnoff memcpy(&ts, &optp[2], sizeof(ts));
1793b3a8eb9SGleb Smirnoff if (ts == 0)
1803b3a8eb9SGleb Smirnoff fp.fp_flags |= PF_OSFP_TS0;
1813b3a8eb9SGleb Smirnoff }
1823b3a8eb9SGleb Smirnoff fp.fp_tcpopts = (fp.fp_tcpopts <<
1833b3a8eb9SGleb Smirnoff PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
1843b3a8eb9SGleb Smirnoff break;
1853b3a8eb9SGleb Smirnoff default:
1863b3a8eb9SGleb Smirnoff return (NULL);
1873b3a8eb9SGleb Smirnoff }
1883b3a8eb9SGleb Smirnoff }
1893b3a8eb9SGleb Smirnoff optlen = MAX(optlen, 1); /* paranoia */
1903b3a8eb9SGleb Smirnoff }
1913b3a8eb9SGleb Smirnoff
1923b3a8eb9SGleb Smirnoff DPFPRINTF("fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) "
1933b3a8eb9SGleb Smirnoff "(TS=%s,M=%s%d,W=%s%d)\n",
1943b3a8eb9SGleb Smirnoff srcname, ntohs(tcp->th_sport),
1953b3a8eb9SGleb Smirnoff fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
1963b3a8eb9SGleb Smirnoff fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
1973b3a8eb9SGleb Smirnoff (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
1983b3a8eb9SGleb Smirnoff (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
1993b3a8eb9SGleb Smirnoff (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
2003b3a8eb9SGleb Smirnoff fp.fp_mss,
2013b3a8eb9SGleb Smirnoff (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
2023b3a8eb9SGleb Smirnoff (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
2033b3a8eb9SGleb Smirnoff fp.fp_wscale);
2043b3a8eb9SGleb Smirnoff
2053b3a8eb9SGleb Smirnoff if ((fpresult = pf_osfp_find(&V_pf_osfp_list, &fp,
2063b3a8eb9SGleb Smirnoff PF_OSFP_MAXTTL_OFFSET)))
2073b3a8eb9SGleb Smirnoff return (&fpresult->fp_oses);
2083b3a8eb9SGleb Smirnoff return (NULL);
2093b3a8eb9SGleb Smirnoff }
2103b3a8eb9SGleb Smirnoff
2113b3a8eb9SGleb Smirnoff /* Match a fingerprint ID against a list of OSes */
2123b3a8eb9SGleb Smirnoff int
pf_osfp_match(struct pf_osfp_enlist * list,pf_osfp_t os)2133b3a8eb9SGleb Smirnoff pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
2143b3a8eb9SGleb Smirnoff {
2153b3a8eb9SGleb Smirnoff struct pf_osfp_entry *entry;
2163b3a8eb9SGleb Smirnoff int os_class, os_version, os_subtype;
2173b3a8eb9SGleb Smirnoff int en_class, en_version, en_subtype;
2183b3a8eb9SGleb Smirnoff
2193b3a8eb9SGleb Smirnoff if (os == PF_OSFP_ANY)
2203b3a8eb9SGleb Smirnoff return (1);
2213b3a8eb9SGleb Smirnoff if (list == NULL) {
2223b3a8eb9SGleb Smirnoff DPFPRINTF("osfp no match against %x\n", os);
2233b3a8eb9SGleb Smirnoff return (os == PF_OSFP_UNKNOWN);
2243b3a8eb9SGleb Smirnoff }
2253b3a8eb9SGleb Smirnoff PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
2263b3a8eb9SGleb Smirnoff SLIST_FOREACH(entry, list, fp_entry) {
2273b3a8eb9SGleb Smirnoff PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
2283b3a8eb9SGleb Smirnoff if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
2293b3a8eb9SGleb Smirnoff (os_version == PF_OSFP_ANY || en_version == os_version) &&
2303b3a8eb9SGleb Smirnoff (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
2313b3a8eb9SGleb Smirnoff DPFPRINTF("osfp matched %s %s %s %x==%x\n",
2323b3a8eb9SGleb Smirnoff entry->fp_class_nm, entry->fp_version_nm,
2333b3a8eb9SGleb Smirnoff entry->fp_subtype_nm, os, entry->fp_os);
2343b3a8eb9SGleb Smirnoff return (1);
2353b3a8eb9SGleb Smirnoff }
2363b3a8eb9SGleb Smirnoff }
2373b3a8eb9SGleb Smirnoff DPFPRINTF("fingerprint 0x%x didn't match\n", os);
2383b3a8eb9SGleb Smirnoff return (0);
2393b3a8eb9SGleb Smirnoff }
2403b3a8eb9SGleb Smirnoff
2413b3a8eb9SGleb Smirnoff /* Flush the fingerprint list */
2423b3a8eb9SGleb Smirnoff void
pf_osfp_flush(void)2433b3a8eb9SGleb Smirnoff pf_osfp_flush(void)
2443b3a8eb9SGleb Smirnoff {
2453b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *fp;
2463b3a8eb9SGleb Smirnoff struct pf_osfp_entry *entry;
2473b3a8eb9SGleb Smirnoff
2483b3a8eb9SGleb Smirnoff while ((fp = SLIST_FIRST(&V_pf_osfp_list))) {
2493b3a8eb9SGleb Smirnoff SLIST_REMOVE_HEAD(&V_pf_osfp_list, fp_next);
2503b3a8eb9SGleb Smirnoff while ((entry = SLIST_FIRST(&fp->fp_oses))) {
2513b3a8eb9SGleb Smirnoff SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
2523b3a8eb9SGleb Smirnoff free(entry, M_PFOSFP);
2533b3a8eb9SGleb Smirnoff }
2543b3a8eb9SGleb Smirnoff free(fp, M_PFOSFP);
2553b3a8eb9SGleb Smirnoff }
2563b3a8eb9SGleb Smirnoff }
2573b3a8eb9SGleb Smirnoff
2583b3a8eb9SGleb Smirnoff /* Add a fingerprint */
2593b3a8eb9SGleb Smirnoff int
pf_osfp_add(struct pf_osfp_ioctl * fpioc)2603b3a8eb9SGleb Smirnoff pf_osfp_add(struct pf_osfp_ioctl *fpioc)
2613b3a8eb9SGleb Smirnoff {
2623b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *fp, fpadd;
2633b3a8eb9SGleb Smirnoff struct pf_osfp_entry *entry;
2643b3a8eb9SGleb Smirnoff
2653b3a8eb9SGleb Smirnoff PF_RULES_WASSERT();
2663b3a8eb9SGleb Smirnoff
2673b3a8eb9SGleb Smirnoff memset(&fpadd, 0, sizeof(fpadd));
2683b3a8eb9SGleb Smirnoff fpadd.fp_tcpopts = fpioc->fp_tcpopts;
2693b3a8eb9SGleb Smirnoff fpadd.fp_wsize = fpioc->fp_wsize;
2703b3a8eb9SGleb Smirnoff fpadd.fp_psize = fpioc->fp_psize;
2713b3a8eb9SGleb Smirnoff fpadd.fp_mss = fpioc->fp_mss;
2723b3a8eb9SGleb Smirnoff fpadd.fp_flags = fpioc->fp_flags;
2733b3a8eb9SGleb Smirnoff fpadd.fp_optcnt = fpioc->fp_optcnt;
2743b3a8eb9SGleb Smirnoff fpadd.fp_wscale = fpioc->fp_wscale;
2753b3a8eb9SGleb Smirnoff fpadd.fp_ttl = fpioc->fp_ttl;
2763b3a8eb9SGleb Smirnoff
2773b3a8eb9SGleb Smirnoff #if 0 /* XXX RYAN wants to fix logging */
2783b3a8eb9SGleb Smirnoff DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
2793b3a8eb9SGleb Smirnoff "(TS=%s,M=%s%d,W=%s%d) %x\n",
2803b3a8eb9SGleb Smirnoff fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
2813b3a8eb9SGleb Smirnoff fpioc->fp_os.fp_subtype_nm,
2823b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
2833b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
2843b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
2853b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
2863b3a8eb9SGleb Smirnoff fpadd.fp_wsize,
2873b3a8eb9SGleb Smirnoff fpadd.fp_ttl,
2883b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
2893b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
2903b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
2913b3a8eb9SGleb Smirnoff fpadd.fp_psize,
2923b3a8eb9SGleb Smirnoff (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
2933b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
2943b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
2953b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
2963b3a8eb9SGleb Smirnoff fpadd.fp_mss,
2973b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
2983b3a8eb9SGleb Smirnoff (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
2993b3a8eb9SGleb Smirnoff fpadd.fp_wscale,
3003b3a8eb9SGleb Smirnoff fpioc->fp_os.fp_os);
3013b3a8eb9SGleb Smirnoff #endif
3023b3a8eb9SGleb Smirnoff
3033b3a8eb9SGleb Smirnoff if ((fp = pf_osfp_find_exact(&V_pf_osfp_list, &fpadd))) {
3043b3a8eb9SGleb Smirnoff SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
3053b3a8eb9SGleb Smirnoff if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
3063b3a8eb9SGleb Smirnoff return (EEXIST);
3073b3a8eb9SGleb Smirnoff }
3083b3a8eb9SGleb Smirnoff if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT))
3093b3a8eb9SGleb Smirnoff == NULL)
3103b3a8eb9SGleb Smirnoff return (ENOMEM);
3113b3a8eb9SGleb Smirnoff } else {
3123b3a8eb9SGleb Smirnoff if ((fp = malloc(sizeof(*fp), M_PFOSFP, M_ZERO | M_NOWAIT))
3133b3a8eb9SGleb Smirnoff == NULL)
3143b3a8eb9SGleb Smirnoff return (ENOMEM);
3153b3a8eb9SGleb Smirnoff fp->fp_tcpopts = fpioc->fp_tcpopts;
3163b3a8eb9SGleb Smirnoff fp->fp_wsize = fpioc->fp_wsize;
3173b3a8eb9SGleb Smirnoff fp->fp_psize = fpioc->fp_psize;
3183b3a8eb9SGleb Smirnoff fp->fp_mss = fpioc->fp_mss;
3193b3a8eb9SGleb Smirnoff fp->fp_flags = fpioc->fp_flags;
3203b3a8eb9SGleb Smirnoff fp->fp_optcnt = fpioc->fp_optcnt;
3213b3a8eb9SGleb Smirnoff fp->fp_wscale = fpioc->fp_wscale;
3223b3a8eb9SGleb Smirnoff fp->fp_ttl = fpioc->fp_ttl;
3233b3a8eb9SGleb Smirnoff SLIST_INIT(&fp->fp_oses);
3243b3a8eb9SGleb Smirnoff if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT))
3253b3a8eb9SGleb Smirnoff == NULL) {
3263b3a8eb9SGleb Smirnoff free(fp, M_PFOSFP);
3273b3a8eb9SGleb Smirnoff return (ENOMEM);
3283b3a8eb9SGleb Smirnoff }
3293b3a8eb9SGleb Smirnoff pf_osfp_insert(&V_pf_osfp_list, fp);
3303b3a8eb9SGleb Smirnoff }
3313b3a8eb9SGleb Smirnoff memcpy(entry, &fpioc->fp_os, sizeof(*entry));
3323b3a8eb9SGleb Smirnoff
3333b3a8eb9SGleb Smirnoff /* Make sure the strings are NUL terminated */
3343b3a8eb9SGleb Smirnoff entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
3353b3a8eb9SGleb Smirnoff entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
3363b3a8eb9SGleb Smirnoff entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
3373b3a8eb9SGleb Smirnoff
3383b3a8eb9SGleb Smirnoff SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
3393b3a8eb9SGleb Smirnoff
3403b3a8eb9SGleb Smirnoff #ifdef PFDEBUG
3413b3a8eb9SGleb Smirnoff if ((fp = pf_osfp_validate()))
3423b3a8eb9SGleb Smirnoff printf("Invalid fingerprint list\n");
3433b3a8eb9SGleb Smirnoff #endif /* PFDEBUG */
3443b3a8eb9SGleb Smirnoff return (0);
3453b3a8eb9SGleb Smirnoff }
3463b3a8eb9SGleb Smirnoff
3473b3a8eb9SGleb Smirnoff /* Find a fingerprint in the list */
3483b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *
pf_osfp_find(struct pf_osfp_list * list,struct pf_os_fingerprint * find,u_int8_t ttldiff)3493b3a8eb9SGleb Smirnoff pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
3503b3a8eb9SGleb Smirnoff u_int8_t ttldiff)
3513b3a8eb9SGleb Smirnoff {
3523b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *f;
3533b3a8eb9SGleb Smirnoff
3543b3a8eb9SGleb Smirnoff #define MATCH_INT(_MOD, _DC, _field) \
3553b3a8eb9SGleb Smirnoff if ((f->fp_flags & _DC) == 0) { \
3563b3a8eb9SGleb Smirnoff if ((f->fp_flags & _MOD) == 0) { \
3573b3a8eb9SGleb Smirnoff if (f->_field != find->_field) \
3583b3a8eb9SGleb Smirnoff continue; \
3593b3a8eb9SGleb Smirnoff } else { \
3603b3a8eb9SGleb Smirnoff if (f->_field == 0 || find->_field % f->_field) \
3613b3a8eb9SGleb Smirnoff continue; \
3623b3a8eb9SGleb Smirnoff } \
3633b3a8eb9SGleb Smirnoff }
3643b3a8eb9SGleb Smirnoff
3653b3a8eb9SGleb Smirnoff SLIST_FOREACH(f, list, fp_next) {
3663b3a8eb9SGleb Smirnoff if (f->fp_tcpopts != find->fp_tcpopts ||
3673b3a8eb9SGleb Smirnoff f->fp_optcnt != find->fp_optcnt ||
3683b3a8eb9SGleb Smirnoff f->fp_ttl < find->fp_ttl ||
3693b3a8eb9SGleb Smirnoff f->fp_ttl - find->fp_ttl > ttldiff ||
3703b3a8eb9SGleb Smirnoff (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
3713b3a8eb9SGleb Smirnoff (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
3723b3a8eb9SGleb Smirnoff continue;
3733b3a8eb9SGleb Smirnoff
3743b3a8eb9SGleb Smirnoff MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
3753b3a8eb9SGleb Smirnoff MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
3763b3a8eb9SGleb Smirnoff MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
3773b3a8eb9SGleb Smirnoff if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
3783b3a8eb9SGleb Smirnoff if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
3793b3a8eb9SGleb Smirnoff if (find->fp_mss == 0)
3803b3a8eb9SGleb Smirnoff continue;
3813b3a8eb9SGleb Smirnoff
3823b3a8eb9SGleb Smirnoff /*
3833b3a8eb9SGleb Smirnoff * Some "smart" NAT devices and DSL routers will tweak the MSS size and
3843b3a8eb9SGleb Smirnoff * will set it to whatever is suitable for the link type.
3853b3a8eb9SGleb Smirnoff */
3863b3a8eb9SGleb Smirnoff #define SMART_MSS 1460
3873b3a8eb9SGleb Smirnoff if ((find->fp_wsize % find->fp_mss ||
3883b3a8eb9SGleb Smirnoff find->fp_wsize / find->fp_mss !=
3893b3a8eb9SGleb Smirnoff f->fp_wsize) &&
3903b3a8eb9SGleb Smirnoff (find->fp_wsize % SMART_MSS ||
3913b3a8eb9SGleb Smirnoff find->fp_wsize / SMART_MSS !=
3923b3a8eb9SGleb Smirnoff f->fp_wsize))
3933b3a8eb9SGleb Smirnoff continue;
3943b3a8eb9SGleb Smirnoff } else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
3953b3a8eb9SGleb Smirnoff if (find->fp_mss == 0)
3963b3a8eb9SGleb Smirnoff continue;
3973b3a8eb9SGleb Smirnoff
3983b3a8eb9SGleb Smirnoff #define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr))
3993b3a8eb9SGleb Smirnoff #define SMART_MTU (SMART_MSS + MTUOFF)
4003b3a8eb9SGleb Smirnoff if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
4013b3a8eb9SGleb Smirnoff find->fp_wsize / (find->fp_mss + MTUOFF) !=
4023b3a8eb9SGleb Smirnoff f->fp_wsize) &&
4033b3a8eb9SGleb Smirnoff (find->fp_wsize % SMART_MTU ||
4043b3a8eb9SGleb Smirnoff find->fp_wsize / SMART_MTU !=
4053b3a8eb9SGleb Smirnoff f->fp_wsize))
4063b3a8eb9SGleb Smirnoff continue;
4073b3a8eb9SGleb Smirnoff } else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
4083b3a8eb9SGleb Smirnoff if (f->fp_wsize == 0 || find->fp_wsize %
4093b3a8eb9SGleb Smirnoff f->fp_wsize)
4103b3a8eb9SGleb Smirnoff continue;
4113b3a8eb9SGleb Smirnoff } else {
4123b3a8eb9SGleb Smirnoff if (f->fp_wsize != find->fp_wsize)
4133b3a8eb9SGleb Smirnoff continue;
4143b3a8eb9SGleb Smirnoff }
4153b3a8eb9SGleb Smirnoff }
4163b3a8eb9SGleb Smirnoff return (f);
4173b3a8eb9SGleb Smirnoff }
4183b3a8eb9SGleb Smirnoff
4193b3a8eb9SGleb Smirnoff return (NULL);
4203b3a8eb9SGleb Smirnoff }
4213b3a8eb9SGleb Smirnoff
4223b3a8eb9SGleb Smirnoff /* Find an exact fingerprint in the list */
4233b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *
pf_osfp_find_exact(struct pf_osfp_list * list,struct pf_os_fingerprint * find)4243b3a8eb9SGleb Smirnoff pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
4253b3a8eb9SGleb Smirnoff {
4263b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *f;
4273b3a8eb9SGleb Smirnoff
4283b3a8eb9SGleb Smirnoff SLIST_FOREACH(f, list, fp_next) {
4293b3a8eb9SGleb Smirnoff if (f->fp_tcpopts == find->fp_tcpopts &&
4303b3a8eb9SGleb Smirnoff f->fp_wsize == find->fp_wsize &&
4313b3a8eb9SGleb Smirnoff f->fp_psize == find->fp_psize &&
4323b3a8eb9SGleb Smirnoff f->fp_mss == find->fp_mss &&
4333b3a8eb9SGleb Smirnoff f->fp_flags == find->fp_flags &&
4343b3a8eb9SGleb Smirnoff f->fp_optcnt == find->fp_optcnt &&
4353b3a8eb9SGleb Smirnoff f->fp_wscale == find->fp_wscale &&
4363b3a8eb9SGleb Smirnoff f->fp_ttl == find->fp_ttl)
4373b3a8eb9SGleb Smirnoff return (f);
4383b3a8eb9SGleb Smirnoff }
4393b3a8eb9SGleb Smirnoff
4403b3a8eb9SGleb Smirnoff return (NULL);
4413b3a8eb9SGleb Smirnoff }
4423b3a8eb9SGleb Smirnoff
4433b3a8eb9SGleb Smirnoff /* Insert a fingerprint into the list */
4443b3a8eb9SGleb Smirnoff static void
pf_osfp_insert(struct pf_osfp_list * list,struct pf_os_fingerprint * ins)4453b3a8eb9SGleb Smirnoff pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
4463b3a8eb9SGleb Smirnoff {
4473b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *f, *prev = NULL;
4483b3a8eb9SGleb Smirnoff
4493b3a8eb9SGleb Smirnoff /* XXX need to go semi tree based. can key on tcp options */
4503b3a8eb9SGleb Smirnoff
4513b3a8eb9SGleb Smirnoff SLIST_FOREACH(f, list, fp_next)
4523b3a8eb9SGleb Smirnoff prev = f;
4533b3a8eb9SGleb Smirnoff if (prev)
4543b3a8eb9SGleb Smirnoff SLIST_INSERT_AFTER(prev, ins, fp_next);
4553b3a8eb9SGleb Smirnoff else
4563b3a8eb9SGleb Smirnoff SLIST_INSERT_HEAD(list, ins, fp_next);
4573b3a8eb9SGleb Smirnoff }
4583b3a8eb9SGleb Smirnoff
4593b3a8eb9SGleb Smirnoff /* Fill a fingerprint by its number (from an ioctl) */
4603b3a8eb9SGleb Smirnoff int
pf_osfp_get(struct pf_osfp_ioctl * fpioc)4613b3a8eb9SGleb Smirnoff pf_osfp_get(struct pf_osfp_ioctl *fpioc)
4623b3a8eb9SGleb Smirnoff {
4633b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *fp;
4643b3a8eb9SGleb Smirnoff struct pf_osfp_entry *entry;
4653b3a8eb9SGleb Smirnoff int num = fpioc->fp_getnum;
4663b3a8eb9SGleb Smirnoff int i = 0;
4673b3a8eb9SGleb Smirnoff
4683b3a8eb9SGleb Smirnoff memset(fpioc, 0, sizeof(*fpioc));
4693b3a8eb9SGleb Smirnoff SLIST_FOREACH(fp, &V_pf_osfp_list, fp_next) {
4703b3a8eb9SGleb Smirnoff SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
4713b3a8eb9SGleb Smirnoff if (i++ == num) {
4723b3a8eb9SGleb Smirnoff fpioc->fp_mss = fp->fp_mss;
4733b3a8eb9SGleb Smirnoff fpioc->fp_wsize = fp->fp_wsize;
4743b3a8eb9SGleb Smirnoff fpioc->fp_flags = fp->fp_flags;
4753b3a8eb9SGleb Smirnoff fpioc->fp_psize = fp->fp_psize;
4763b3a8eb9SGleb Smirnoff fpioc->fp_ttl = fp->fp_ttl;
4773b3a8eb9SGleb Smirnoff fpioc->fp_wscale = fp->fp_wscale;
4783b3a8eb9SGleb Smirnoff fpioc->fp_getnum = num;
4793b3a8eb9SGleb Smirnoff memcpy(&fpioc->fp_os, entry,
4803b3a8eb9SGleb Smirnoff sizeof(fpioc->fp_os));
4813b3a8eb9SGleb Smirnoff return (0);
4823b3a8eb9SGleb Smirnoff }
4833b3a8eb9SGleb Smirnoff }
4843b3a8eb9SGleb Smirnoff }
4853b3a8eb9SGleb Smirnoff
4863b3a8eb9SGleb Smirnoff return (EBUSY);
4873b3a8eb9SGleb Smirnoff }
4883b3a8eb9SGleb Smirnoff
4893b3a8eb9SGleb Smirnoff #ifdef PFDEBUG
4903b3a8eb9SGleb Smirnoff /* Validate that each signature is reachable */
4913b3a8eb9SGleb Smirnoff static struct pf_os_fingerprint *
pf_osfp_validate(void)4923b3a8eb9SGleb Smirnoff pf_osfp_validate(void)
4933b3a8eb9SGleb Smirnoff {
4943b3a8eb9SGleb Smirnoff struct pf_os_fingerprint *f, *f2, find;
4953b3a8eb9SGleb Smirnoff
4963b3a8eb9SGleb Smirnoff SLIST_FOREACH(f, &V_pf_osfp_list, fp_next) {
4973b3a8eb9SGleb Smirnoff memcpy(&find, f, sizeof(find));
4983b3a8eb9SGleb Smirnoff
4993b3a8eb9SGleb Smirnoff /* We do a few MSS/th_win percolations to make things unique */
5003b3a8eb9SGleb Smirnoff if (find.fp_mss == 0)
5013b3a8eb9SGleb Smirnoff find.fp_mss = 128;
5023b3a8eb9SGleb Smirnoff if (f->fp_flags & PF_OSFP_WSIZE_MSS)
5033b3a8eb9SGleb Smirnoff find.fp_wsize *= find.fp_mss;
5043b3a8eb9SGleb Smirnoff else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
5053b3a8eb9SGleb Smirnoff find.fp_wsize *= (find.fp_mss + 40);
5063b3a8eb9SGleb Smirnoff else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
5073b3a8eb9SGleb Smirnoff find.fp_wsize *= 2;
5083b3a8eb9SGleb Smirnoff if (f != (f2 = pf_osfp_find(&V_pf_osfp_list, &find, 0))) {
5093b3a8eb9SGleb Smirnoff if (f2)
5103b3a8eb9SGleb Smirnoff printf("Found \"%s %s %s\" instead of "
5113b3a8eb9SGleb Smirnoff "\"%s %s %s\"\n",
5123b3a8eb9SGleb Smirnoff SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
5133b3a8eb9SGleb Smirnoff SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
5143b3a8eb9SGleb Smirnoff SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
5153b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_class_nm,
5163b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_version_nm,
5173b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
5183b3a8eb9SGleb Smirnoff else
5193b3a8eb9SGleb Smirnoff printf("Couldn't find \"%s %s %s\"\n",
5203b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_class_nm,
5213b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_version_nm,
5223b3a8eb9SGleb Smirnoff SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
5233b3a8eb9SGleb Smirnoff return (f);
5243b3a8eb9SGleb Smirnoff }
5253b3a8eb9SGleb Smirnoff }
5263b3a8eb9SGleb Smirnoff return (NULL);
5273b3a8eb9SGleb Smirnoff }
5283b3a8eb9SGleb Smirnoff #endif /* PFDEBUG */
529