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