Home | History | Annotate | Line # | Download | only in net
pf_osfp.c revision 1.6.26.1
      1  1.6.26.1  bouyer /*	$NetBSD: pf_osfp.c,v 1.6.26.1 2007/12/13 21:56:50 bouyer Exp $	*/
      2       1.4    yamt /*	$OpenBSD: pf_osfp.c,v 1.10 2004/04/09 19:30:41 frantzen Exp $ */
      3       1.1  itojun 
      4       1.1  itojun /*
      5       1.1  itojun  * Copyright (c) 2003 Mike Frantzen <frantzen (at) w4g.org>
      6       1.1  itojun  *
      7       1.1  itojun  * Permission to use, copy, modify, and distribute this software for any
      8       1.1  itojun  * purpose with or without fee is hereby granted, provided that the above
      9       1.1  itojun  * copyright notice and this permission notice appear in all copies.
     10       1.1  itojun  *
     11       1.1  itojun  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12       1.1  itojun  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13       1.1  itojun  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14       1.1  itojun  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15       1.1  itojun  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16       1.1  itojun  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17       1.1  itojun  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18       1.1  itojun  *
     19       1.1  itojun  */
     20       1.1  itojun 
     21  1.6.26.1  bouyer #include <sys/cdefs.h>
     22  1.6.26.1  bouyer __KERNEL_RCSID(0, "$NetBSD: pf_osfp.c,v 1.6.26.1 2007/12/13 21:56:50 bouyer Exp $");
     23  1.6.26.1  bouyer 
     24       1.2  itojun #ifdef _KERNEL_OPT
     25       1.2  itojun #include "opt_inet.h"
     26       1.2  itojun #endif
     27       1.2  itojun 
     28       1.1  itojun #include <sys/param.h>
     29       1.1  itojun #include <sys/socket.h>
     30       1.1  itojun #ifdef _KERNEL
     31       1.1  itojun # include <sys/systm.h>
     32       1.1  itojun #endif /* _KERNEL */
     33       1.1  itojun #include <sys/mbuf.h>
     34       1.1  itojun 
     35       1.1  itojun #include <netinet/in.h>
     36       1.1  itojun #include <netinet/in_systm.h>
     37       1.1  itojun #include <netinet/ip.h>
     38       1.1  itojun #include <netinet/tcp.h>
     39       1.1  itojun 
     40       1.1  itojun #include <net/if.h>
     41       1.1  itojun #include <net/pfvar.h>
     42       1.1  itojun 
     43       1.1  itojun #ifdef INET6
     44       1.1  itojun #include <netinet/ip6.h>
     45       1.1  itojun #endif /* INET6 */
     46       1.1  itojun 
     47       1.1  itojun 
     48       1.1  itojun #ifdef _KERNEL
     49       1.1  itojun # define DPFPRINTF(format, x...)		\
     50       1.1  itojun 	if (pf_status.debug >= PF_DEBUG_NOISY)	\
     51       1.1  itojun 		printf(format , ##x)
     52       1.1  itojun typedef struct pool pool_t;
     53       1.1  itojun 
     54       1.1  itojun #else
     55       1.1  itojun /* Userland equivalents so we can lend code to tcpdump et al. */
     56       1.1  itojun 
     57       1.1  itojun # include <arpa/inet.h>
     58       1.1  itojun # include <errno.h>
     59       1.1  itojun # include <stdio.h>
     60       1.1  itojun # include <stdlib.h>
     61       1.1  itojun # include <string.h>
     62       1.1  itojun # define pool_t			int
     63       1.1  itojun # define pool_get(pool, flags)	malloc(*(pool))
     64       1.1  itojun # define pool_put(pool, item)	free(item)
     65       1.1  itojun # define pool_init(pool, size, a, ao, f, m, p)	(*(pool)) = (size)
     66       1.1  itojun 
     67       1.1  itojun # ifdef PFDEBUG
     68       1.1  itojun #  include <sys/stdarg.h>
     69       1.1  itojun #  define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
     70       1.1  itojun # else
     71       1.1  itojun #  define DPFPRINTF(format, x...)	((void)0)
     72       1.1  itojun # endif /* PFDEBUG */
     73       1.1  itojun #endif /* _KERNEL */
     74       1.1  itojun 
     75       1.1  itojun 
     76       1.1  itojun SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
     77       1.1  itojun pool_t pf_osfp_entry_pl;
     78       1.1  itojun pool_t pf_osfp_pl;
     79       1.1  itojun 
     80       1.1  itojun struct pf_os_fingerprint	*pf_osfp_find(struct pf_osfp_list *,
     81       1.1  itojun 				    struct pf_os_fingerprint *, u_int8_t);
     82       1.1  itojun struct pf_os_fingerprint	*pf_osfp_find_exact(struct pf_osfp_list *,
     83       1.1  itojun 				    struct pf_os_fingerprint *);
     84       1.1  itojun void				 pf_osfp_insert(struct pf_osfp_list *,
     85       1.1  itojun 				    struct pf_os_fingerprint *);
     86       1.1  itojun 
     87       1.1  itojun 
     88       1.1  itojun #ifdef _KERNEL
     89       1.1  itojun /*
     90       1.1  itojun  * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
     91       1.1  itojun  * Returns the list of possible OSes.
     92       1.1  itojun  */
     93       1.1  itojun struct pf_osfp_enlist *
     94       1.1  itojun pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off,
     95       1.1  itojun     const struct tcphdr *tcp)
     96       1.1  itojun {
     97       1.1  itojun 	struct ip *ip;
     98       1.1  itojun 	char hdr[60];
     99       1.1  itojun 
    100       1.1  itojun 	/* XXX don't have a fingerprint database for IPv6 :-( */
    101       1.1  itojun 	if (pd->af != PF_INET || pd->proto != IPPROTO_TCP || (tcp->th_off << 2)
    102       1.1  itojun 	    < sizeof(*tcp))
    103       1.1  itojun 		return (NULL);
    104       1.1  itojun 
    105       1.1  itojun 	ip = mtod(m, struct ip *);
    106       1.1  itojun 	if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL, pd->af))
    107       1.1  itojun 		return (NULL);
    108       1.1  itojun 
    109       1.1  itojun 	return (pf_osfp_fingerprint_hdr(ip, (struct tcphdr *)hdr));
    110       1.1  itojun }
    111       1.1  itojun #endif /* _KERNEL */
    112       1.1  itojun 
    113       1.1  itojun struct pf_osfp_enlist *
    114       1.1  itojun pf_osfp_fingerprint_hdr(const struct ip *ip, const struct tcphdr *tcp)
    115       1.1  itojun {
    116       1.1  itojun 	struct pf_os_fingerprint fp, *fpresult;
    117       1.1  itojun 	int cnt, optlen = 0;
    118       1.1  itojun 	const u_int8_t *optp;
    119       1.1  itojun 
    120       1.1  itojun 	if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN || (ip->ip_off &
    121       1.1  itojun 	    htons(IP_OFFMASK)))
    122       1.1  itojun 		return (NULL);
    123       1.1  itojun 
    124       1.1  itojun 	memset(&fp, 0, sizeof(fp));
    125       1.1  itojun 
    126       1.1  itojun 	fp.fp_psize = ntohs(ip->ip_len);
    127       1.1  itojun 	fp.fp_ttl = ip->ip_ttl;
    128       1.1  itojun 	if (ip->ip_off & htons(IP_DF))
    129       1.1  itojun 		fp.fp_flags |= PF_OSFP_DF;
    130       1.1  itojun 	fp.fp_wsize = ntohs(tcp->th_win);
    131       1.1  itojun 
    132       1.1  itojun 
    133       1.1  itojun 	cnt = (tcp->th_off << 2) - sizeof(*tcp);
    134       1.1  itojun 	optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
    135       1.1  itojun 	for (; cnt > 0; cnt -= optlen, optp += optlen) {
    136       1.1  itojun 		if (*optp == TCPOPT_EOL)
    137       1.1  itojun 			break;
    138       1.1  itojun 
    139       1.1  itojun 		fp.fp_optcnt++;
    140       1.1  itojun 		if (*optp == TCPOPT_NOP) {
    141       1.1  itojun 			fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
    142       1.1  itojun 			    PF_OSFP_TCPOPT_NOP;
    143       1.1  itojun 			optlen = 1;
    144       1.1  itojun 		} else {
    145       1.1  itojun 			if (cnt < 2)
    146       1.1  itojun 				return (NULL);
    147       1.1  itojun 			optlen = optp[1];
    148       1.1  itojun 			if (optlen > cnt || optlen < 2)
    149       1.1  itojun 				return (NULL);
    150       1.1  itojun 			switch (*optp) {
    151       1.1  itojun 			case TCPOPT_MAXSEG:
    152       1.1  itojun 				if (optlen >= TCPOLEN_MAXSEG)
    153       1.1  itojun 					memcpy(&fp.fp_mss, &optp[2],
    154       1.1  itojun 					    sizeof(fp.fp_mss));
    155       1.1  itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    156       1.1  itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
    157       1.1  itojun 				NTOHS(fp.fp_mss);
    158       1.1  itojun 				break;
    159       1.1  itojun 			case TCPOPT_WINDOW:
    160       1.1  itojun 				if (optlen >= TCPOLEN_WINDOW)
    161       1.1  itojun 					memcpy(&fp.fp_wscale, &optp[2],
    162       1.1  itojun 					    sizeof(fp.fp_wscale));
    163       1.1  itojun 				NTOHS(fp.fp_wscale);
    164       1.1  itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    165       1.1  itojun 				    PF_OSFP_TCPOPT_BITS) |
    166       1.1  itojun 				    PF_OSFP_TCPOPT_WSCALE;
    167       1.1  itojun 				break;
    168       1.1  itojun 			case TCPOPT_SACK_PERMITTED:
    169       1.1  itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    170       1.1  itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
    171       1.1  itojun 				break;
    172       1.1  itojun 			case TCPOPT_TIMESTAMP:
    173       1.1  itojun 				if (optlen >= TCPOLEN_TIMESTAMP) {
    174       1.1  itojun 					u_int32_t ts;
    175       1.1  itojun 					memcpy(&ts, &optp[2], sizeof(ts));
    176       1.1  itojun 					if (ts == 0)
    177       1.1  itojun 						fp.fp_flags |= PF_OSFP_TS0;
    178       1.1  itojun 
    179       1.1  itojun 				}
    180       1.1  itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    181       1.1  itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
    182       1.1  itojun 				break;
    183       1.1  itojun 			default:
    184       1.1  itojun 				return (NULL);
    185       1.1  itojun 			}
    186       1.1  itojun 		}
    187       1.1  itojun 		optlen = MAX(optlen, 1);	/* paranoia */
    188       1.1  itojun 	}
    189       1.1  itojun 
    190       1.1  itojun 	DPFPRINTF("fingerprinted %s:%d  %d:%d:%d:%d:%llx (%d) "
    191       1.1  itojun 	    "(TS=%s,M=%s%d,W=%s%d)\n",
    192       1.1  itojun 	    inet_ntoa(ip->ip_src), ntohs(tcp->th_sport),
    193       1.1  itojun 	    fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
    194       1.1  itojun 	    fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
    195       1.1  itojun 	    (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
    196       1.1  itojun 	    (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
    197       1.1  itojun 	    (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
    198       1.1  itojun 	    fp.fp_mss,
    199       1.1  itojun 	    (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
    200       1.1  itojun 	    (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
    201       1.1  itojun 	    fp.fp_wscale);
    202       1.1  itojun 
    203       1.1  itojun 	if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
    204       1.1  itojun 	    PF_OSFP_MAXTTL_OFFSET)))
    205       1.1  itojun 		return (&fpresult->fp_oses);
    206       1.1  itojun 	return (NULL);
    207       1.1  itojun }
    208       1.1  itojun 
    209       1.1  itojun /* Match a fingerprint ID against a list of OSes */
    210       1.1  itojun int
    211       1.1  itojun pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
    212       1.1  itojun {
    213       1.1  itojun 	struct pf_osfp_entry *entry;
    214       1.1  itojun 	int os_class, os_version, os_subtype;
    215       1.1  itojun 	int en_class, en_version, en_subtype;
    216       1.1  itojun 
    217       1.1  itojun 	if (os == PF_OSFP_ANY)
    218       1.1  itojun 		return (1);
    219       1.1  itojun 	if (list == NULL) {
    220       1.1  itojun 		DPFPRINTF("osfp no match against %x\n", os);
    221       1.1  itojun 		return (os == PF_OSFP_UNKNOWN);
    222       1.1  itojun 	}
    223       1.1  itojun 	PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
    224       1.1  itojun 	SLIST_FOREACH(entry, list, fp_entry) {
    225       1.1  itojun 		PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
    226       1.1  itojun 		if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
    227       1.1  itojun 		    (os_version == PF_OSFP_ANY || en_version == os_version) &&
    228       1.1  itojun 		    (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
    229       1.1  itojun 			DPFPRINTF("osfp matched %s %s %s  %x==%x\n",
    230       1.1  itojun 			    entry->fp_class_nm, entry->fp_version_nm,
    231       1.1  itojun 			    entry->fp_subtype_nm, os, entry->fp_os);
    232       1.1  itojun 			return (1);
    233       1.1  itojun 		}
    234       1.1  itojun 	}
    235       1.1  itojun 	DPFPRINTF("fingerprint 0x%x didn't match\n", os);
    236       1.1  itojun 	return (0);
    237       1.1  itojun }
    238       1.1  itojun 
    239       1.1  itojun /* Initialize the OS fingerprint system */
    240       1.1  itojun void
    241       1.1  itojun pf_osfp_initialize(void)
    242       1.1  itojun {
    243       1.6      ad #ifdef __NetBSD__
    244       1.6      ad 	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
    245       1.6      ad 	    "pfosfpen", &pool_allocator_nointr, IPL_NONE);
    246       1.6      ad 	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
    247       1.6      ad 	    "pfosfp", &pool_allocator_nointr, IPL_NONE);
    248       1.6      ad #else
    249       1.1  itojun 	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
    250       1.4    yamt 	    "pfosfpen", &pool_allocator_nointr);
    251       1.1  itojun 	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
    252       1.4    yamt 	    "pfosfp", &pool_allocator_nointr);
    253       1.6      ad #endif
    254       1.1  itojun 	SLIST_INIT(&pf_osfp_list);
    255       1.1  itojun }
    256       1.1  itojun 
    257       1.3  itojun #ifdef _LKM
    258       1.3  itojun void
    259       1.3  itojun pf_osfp_destroy(void)
    260       1.3  itojun {
    261       1.3  itojun 	pf_osfp_flush();
    262       1.3  itojun 
    263       1.3  itojun 	pool_destroy(&pf_osfp_pl);
    264       1.3  itojun 	pool_destroy(&pf_osfp_entry_pl);
    265       1.3  itojun }
    266       1.3  itojun #endif
    267       1.3  itojun 
    268       1.1  itojun /* Flush the fingerprint list */
    269       1.1  itojun void
    270       1.1  itojun pf_osfp_flush(void)
    271       1.1  itojun {
    272       1.1  itojun 	struct pf_os_fingerprint *fp;
    273       1.1  itojun 	struct pf_osfp_entry *entry;
    274       1.1  itojun 
    275       1.1  itojun 	while ((fp = SLIST_FIRST(&pf_osfp_list))) {
    276       1.1  itojun 		SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
    277       1.1  itojun 		while ((entry = SLIST_FIRST(&fp->fp_oses))) {
    278       1.1  itojun 			SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
    279       1.1  itojun 			pool_put(&pf_osfp_entry_pl, entry);
    280       1.1  itojun 		}
    281       1.1  itojun 		pool_put(&pf_osfp_pl, fp);
    282       1.1  itojun 	}
    283       1.1  itojun }
    284       1.1  itojun 
    285       1.1  itojun 
    286       1.1  itojun /* Add a fingerprint */
    287       1.1  itojun int
    288       1.1  itojun pf_osfp_add(struct pf_osfp_ioctl *fpioc)
    289       1.1  itojun {
    290       1.1  itojun 	struct pf_os_fingerprint *fp, fpadd;
    291       1.1  itojun 	struct pf_osfp_entry *entry;
    292       1.1  itojun 
    293       1.1  itojun 	memset(&fpadd, 0, sizeof(fpadd));
    294       1.1  itojun 	fpadd.fp_tcpopts = fpioc->fp_tcpopts;
    295       1.1  itojun 	fpadd.fp_wsize = fpioc->fp_wsize;
    296       1.1  itojun 	fpadd.fp_psize = fpioc->fp_psize;
    297       1.1  itojun 	fpadd.fp_mss = fpioc->fp_mss;
    298       1.1  itojun 	fpadd.fp_flags = fpioc->fp_flags;
    299       1.1  itojun 	fpadd.fp_optcnt = fpioc->fp_optcnt;
    300       1.1  itojun 	fpadd.fp_wscale = fpioc->fp_wscale;
    301       1.1  itojun 	fpadd.fp_ttl = fpioc->fp_ttl;
    302       1.1  itojun 
    303       1.1  itojun 	DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
    304       1.1  itojun 	    "(TS=%s,M=%s%d,W=%s%d) %x\n",
    305       1.1  itojun 	    fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
    306       1.1  itojun 	    fpioc->fp_os.fp_subtype_nm,
    307       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
    308       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
    309       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
    310       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
    311       1.1  itojun 	    fpadd.fp_wsize,
    312       1.1  itojun 	    fpadd.fp_ttl,
    313       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
    314       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
    315       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
    316       1.1  itojun 	    fpadd.fp_psize,
    317       1.1  itojun 	    (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
    318       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
    319       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
    320       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
    321       1.1  itojun 	    fpadd.fp_mss,
    322       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
    323       1.1  itojun 	    (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
    324       1.1  itojun 	    fpadd.fp_wscale,
    325       1.1  itojun 	    fpioc->fp_os.fp_os);
    326       1.1  itojun 
    327       1.1  itojun 
    328       1.1  itojun 	if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
    329       1.1  itojun 		 SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
    330       1.1  itojun 			if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
    331       1.1  itojun 				return (EEXIST);
    332       1.1  itojun 		}
    333       1.1  itojun 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL)
    334       1.1  itojun 			return (ENOMEM);
    335       1.1  itojun 	} else {
    336       1.1  itojun 		if ((fp = pool_get(&pf_osfp_pl, PR_NOWAIT)) == NULL)
    337       1.1  itojun 			return (ENOMEM);
    338       1.1  itojun 		memset(fp, 0, sizeof(*fp));
    339       1.1  itojun 		fp->fp_tcpopts = fpioc->fp_tcpopts;
    340       1.1  itojun 		fp->fp_wsize = fpioc->fp_wsize;
    341       1.1  itojun 		fp->fp_psize = fpioc->fp_psize;
    342       1.1  itojun 		fp->fp_mss = fpioc->fp_mss;
    343       1.1  itojun 		fp->fp_flags = fpioc->fp_flags;
    344       1.1  itojun 		fp->fp_optcnt = fpioc->fp_optcnt;
    345       1.1  itojun 		fp->fp_wscale = fpioc->fp_wscale;
    346       1.1  itojun 		fp->fp_ttl = fpioc->fp_ttl;
    347       1.1  itojun 		SLIST_INIT(&fp->fp_oses);
    348       1.1  itojun 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL) {
    349       1.1  itojun 			pool_put(&pf_osfp_pl, fp);
    350       1.1  itojun 			return (ENOMEM);
    351       1.1  itojun 		}
    352       1.1  itojun 		pf_osfp_insert(&pf_osfp_list, fp);
    353       1.1  itojun 	}
    354       1.1  itojun 	memcpy(entry, &fpioc->fp_os, sizeof(*entry));
    355       1.1  itojun 
    356       1.1  itojun 	/* Make sure the strings are NUL terminated */
    357       1.1  itojun 	entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
    358       1.1  itojun 	entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
    359       1.1  itojun 	entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
    360       1.1  itojun 
    361       1.1  itojun 	SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
    362       1.1  itojun 
    363       1.1  itojun #ifdef PFDEBUG
    364       1.1  itojun 	if ((fp = pf_osfp_validate()))
    365       1.1  itojun 		printf("Invalid fingerprint list\n");
    366       1.1  itojun #endif /* PFDEBUG */
    367       1.1  itojun 	return (0);
    368       1.1  itojun }
    369       1.1  itojun 
    370       1.1  itojun 
    371       1.1  itojun /* Find a fingerprint in the list */
    372       1.1  itojun struct pf_os_fingerprint *
    373       1.1  itojun pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
    374       1.1  itojun     u_int8_t ttldiff)
    375       1.1  itojun {
    376       1.1  itojun 	struct pf_os_fingerprint *f;
    377       1.1  itojun 
    378       1.1  itojun #define MATCH_INT(_MOD, _DC, _field)					\
    379       1.1  itojun 	if ((f->fp_flags & _DC) == 0) {					\
    380       1.1  itojun 		if ((f->fp_flags & _MOD) == 0) {			\
    381       1.1  itojun 			if (f->_field != find->_field)			\
    382       1.1  itojun 				continue;				\
    383       1.1  itojun 		} else {						\
    384       1.1  itojun 			if (f->_field == 0 || find->_field % f->_field)	\
    385       1.1  itojun 				continue;				\
    386       1.1  itojun 		}							\
    387       1.1  itojun 	}
    388       1.1  itojun 
    389       1.1  itojun 	SLIST_FOREACH(f, list, fp_next) {
    390       1.1  itojun 		if (f->fp_tcpopts != find->fp_tcpopts ||
    391       1.1  itojun 		    f->fp_optcnt != find->fp_optcnt ||
    392       1.1  itojun 		    f->fp_ttl < find->fp_ttl ||
    393       1.1  itojun 		    f->fp_ttl - find->fp_ttl > ttldiff ||
    394       1.1  itojun 		    (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
    395       1.1  itojun 		    (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
    396       1.1  itojun 			continue;
    397       1.1  itojun 
    398       1.1  itojun 		MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
    399       1.1  itojun 		MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
    400       1.1  itojun 		MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
    401       1.1  itojun 		if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
    402       1.1  itojun 			if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
    403       1.1  itojun 				if (find->fp_mss == 0)
    404       1.1  itojun 					continue;
    405       1.1  itojun 
    406       1.1  itojun /* Some "smart" NAT devices and DSL routers will tweak the MSS size and
    407       1.1  itojun  * will set it to whatever is suitable for the link type.
    408       1.1  itojun  */
    409       1.1  itojun #define SMART_MSS	1460
    410       1.1  itojun 				if ((find->fp_wsize % find->fp_mss ||
    411       1.1  itojun 				    find->fp_wsize / find->fp_mss !=
    412       1.1  itojun 				    f->fp_wsize) &&
    413       1.1  itojun 				    (find->fp_wsize % SMART_MSS ||
    414       1.1  itojun 				    find->fp_wsize / SMART_MSS !=
    415       1.1  itojun 				    f->fp_wsize))
    416       1.1  itojun 					continue;
    417       1.1  itojun 			} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
    418       1.1  itojun 				if (find->fp_mss == 0)
    419       1.1  itojun 					continue;
    420       1.1  itojun 
    421       1.1  itojun #define MTUOFF	(sizeof(struct ip) + sizeof(struct tcphdr))
    422       1.1  itojun #define SMART_MTU	(SMART_MSS + MTUOFF)
    423       1.1  itojun 				if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
    424       1.1  itojun 				    find->fp_wsize / (find->fp_mss + MTUOFF) !=
    425       1.1  itojun 				    f->fp_wsize) &&
    426       1.1  itojun 				    (find->fp_wsize % SMART_MTU ||
    427       1.1  itojun 				    find->fp_wsize / SMART_MTU !=
    428       1.1  itojun 				    f->fp_wsize))
    429       1.1  itojun 					continue;
    430       1.1  itojun 			} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
    431       1.1  itojun 				if (f->fp_wsize == 0 || find->fp_wsize %
    432       1.1  itojun 				    f->fp_wsize)
    433       1.1  itojun 					continue;
    434       1.1  itojun 			} else {
    435       1.1  itojun 				if (f->fp_wsize != find->fp_wsize)
    436       1.1  itojun 					continue;
    437       1.1  itojun 			}
    438       1.1  itojun 		}
    439       1.1  itojun 		return (f);
    440       1.1  itojun 	}
    441       1.1  itojun 
    442       1.1  itojun 	return (NULL);
    443       1.1  itojun }
    444       1.1  itojun 
    445       1.1  itojun /* Find an exact fingerprint in the list */
    446       1.1  itojun struct pf_os_fingerprint *
    447       1.1  itojun pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
    448       1.1  itojun {
    449       1.1  itojun 	struct pf_os_fingerprint *f;
    450       1.1  itojun 
    451       1.1  itojun 	SLIST_FOREACH(f, list, fp_next) {
    452       1.1  itojun 		if (f->fp_tcpopts == find->fp_tcpopts &&
    453       1.1  itojun 		    f->fp_wsize == find->fp_wsize &&
    454       1.1  itojun 		    f->fp_psize == find->fp_psize &&
    455       1.1  itojun 		    f->fp_mss == find->fp_mss &&
    456       1.1  itojun 		    f->fp_flags == find->fp_flags &&
    457       1.1  itojun 		    f->fp_optcnt == find->fp_optcnt &&
    458       1.1  itojun 		    f->fp_wscale == find->fp_wscale &&
    459       1.1  itojun 		    f->fp_ttl == find->fp_ttl)
    460       1.1  itojun 			return (f);
    461       1.1  itojun 	}
    462       1.1  itojun 
    463       1.1  itojun 	return (NULL);
    464       1.1  itojun }
    465       1.1  itojun 
    466       1.1  itojun /* Insert a fingerprint into the list */
    467       1.1  itojun void
    468       1.1  itojun pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
    469       1.1  itojun {
    470       1.1  itojun 	struct pf_os_fingerprint *f, *prev = NULL;
    471       1.1  itojun 
    472       1.1  itojun 	/* XXX need to go semi tree based.  can key on tcp options */
    473       1.1  itojun 
    474       1.1  itojun 	SLIST_FOREACH(f, list, fp_next)
    475       1.1  itojun 		prev = f;
    476       1.1  itojun 	if (prev)
    477       1.1  itojun 		SLIST_INSERT_AFTER(prev, ins, fp_next);
    478       1.1  itojun 	else
    479       1.1  itojun 		SLIST_INSERT_HEAD(list, ins, fp_next);
    480       1.1  itojun }
    481       1.1  itojun 
    482       1.1  itojun /* Fill a fingerprint by its number (from an ioctl) */
    483       1.1  itojun int
    484       1.1  itojun pf_osfp_get(struct pf_osfp_ioctl *fpioc)
    485       1.1  itojun {
    486       1.1  itojun 	struct pf_os_fingerprint *fp;
    487       1.1  itojun 	struct pf_osfp_entry *entry;
    488       1.1  itojun 	int num = fpioc->fp_getnum;
    489       1.1  itojun 	int i = 0;
    490       1.1  itojun 
    491       1.1  itojun 
    492       1.1  itojun 	memset(fpioc, 0, sizeof(*fpioc));
    493       1.1  itojun 	SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
    494       1.1  itojun 		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
    495       1.1  itojun 			if (i++ == num) {
    496       1.1  itojun 				fpioc->fp_mss = fp->fp_mss;
    497       1.1  itojun 				fpioc->fp_wsize = fp->fp_wsize;
    498       1.1  itojun 				fpioc->fp_flags = fp->fp_flags;
    499       1.1  itojun 				fpioc->fp_psize = fp->fp_psize;
    500       1.1  itojun 				fpioc->fp_ttl = fp->fp_ttl;
    501       1.1  itojun 				fpioc->fp_wscale = fp->fp_wscale;
    502       1.1  itojun 				fpioc->fp_getnum = num;
    503       1.1  itojun 				memcpy(&fpioc->fp_os, entry,
    504       1.1  itojun 				    sizeof(fpioc->fp_os));
    505       1.1  itojun 				return (0);
    506       1.1  itojun 			}
    507       1.1  itojun 		}
    508       1.1  itojun 	}
    509       1.1  itojun 
    510       1.1  itojun 	return (EBUSY);
    511       1.1  itojun }
    512       1.1  itojun 
    513       1.1  itojun 
    514       1.1  itojun /* Validate that each signature is reachable */
    515       1.1  itojun struct pf_os_fingerprint *
    516       1.1  itojun pf_osfp_validate(void)
    517       1.1  itojun {
    518       1.1  itojun 	struct pf_os_fingerprint *f, *f2, find;
    519       1.1  itojun 
    520       1.1  itojun 	SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
    521       1.1  itojun 		memcpy(&find, f, sizeof(find));
    522       1.1  itojun 
    523       1.1  itojun 		/* We do a few MSS/th_win percolations to make things unique */
    524       1.1  itojun 		if (find.fp_mss == 0)
    525       1.1  itojun 			find.fp_mss = 128;
    526       1.1  itojun 		if (f->fp_flags & PF_OSFP_WSIZE_MSS)
    527       1.1  itojun 			find.fp_wsize *= find.fp_mss, 1;
    528       1.1  itojun 		else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
    529       1.1  itojun 			find.fp_wsize *= (find.fp_mss + 40);
    530       1.1  itojun 		else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
    531       1.1  itojun 			find.fp_wsize *= 2;
    532       1.1  itojun 		if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
    533       1.1  itojun 			if (f2)
    534       1.1  itojun 				printf("Found \"%s %s %s\" instead of "
    535       1.1  itojun 				    "\"%s %s %s\"\n",
    536       1.1  itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
    537       1.1  itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
    538       1.1  itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
    539       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
    540       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
    541       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
    542       1.1  itojun 			else
    543       1.1  itojun 				printf("Couldn't find \"%s %s %s\"\n",
    544       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
    545       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
    546       1.1  itojun 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
    547       1.1  itojun 			return (f);
    548       1.1  itojun 		}
    549       1.1  itojun 	}
    550       1.1  itojun 	return (NULL);
    551       1.1  itojun }
    552