GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/tcpdump/../../sys/net/pf_osfp.c Lines: 0 210 0.0 %
Date: 2017-11-07 Branches: 0 193 0.0 %

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