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