xref: /xnu-11215/bsd/net/pf_osfp.c (revision 8d741a5d)
1 /*
2  * Copyright (c) 2007-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 /*	$apfw: pf_osfp.c,v 1.4 2008/08/27 00:01:32 jhw Exp $ */
30 /*	$OpenBSD: pf_osfp.c,v 1.12 2006/12/13 18:14:10 itojun Exp $ */
31 
32 /*
33  * Copyright (c) 2003 Mike Frantzen <[email protected]>
34  *
35  * Permission to use, copy, modify, and distribute this software for any
36  * purpose with or without fee is hereby granted, provided that the above
37  * copyright notice and this permission notice appear in all copies.
38  *
39  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
40  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
41  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
42  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
44  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
45  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  *
47  */
48 
49 #include <machine/endian.h>
50 #include <sys/param.h>
51 #include <sys/socket.h>
52 #include <sys/systm.h>
53 #include <sys/mbuf.h>
54 
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 #include <netinet/tcp.h>
59 #include <netinet/tcp_fsm.h>
60 
61 #include <net/if.h>
62 #include <net/pfvar.h>
63 
64 #include <netinet/ip6.h>
65 #include <netinet6/in6_var.h>
66 
67 #define DPFPRINTF(format, x...)                 \
68 	if (pf_status.debug >= PF_DEBUG_NOISY)  \
69 	        printf(format, ##x)
70 
71 static SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
72 static struct pool pf_osfp_entry_pl;
73 static struct pool pf_osfp_pl;
74 
75 static struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *,
76     struct pf_os_fingerprint *, u_int8_t);
77 static struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *,
78     struct pf_os_fingerprint *);
79 static void pf_osfp_insert(struct pf_osfp_list *, struct pf_os_fingerprint *);
80 
81 
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 *
pf_osfp_fingerprint(struct pf_pdesc * pd,pbuf_t * pbuf,int off,const struct tcphdr * tcp)87 pf_osfp_fingerprint(struct pf_pdesc *pd, pbuf_t *pbuf, int off,
88     const struct tcphdr *tcp)
89 {
90 	struct ip *__single ip;
91 	struct ip6_hdr *__single ip6;
92 	char hdr[60];
93 
94 	if ((pd->af != PF_INET && pd->af != PF_INET6) ||
95 	    pd->proto != IPPROTO_TCP ||
96 	    (tcp->th_off << 2) < (int)sizeof(*tcp)) {
97 		return NULL;
98 	}
99 
100 	if (pd->af == PF_INET) {
101 		ip = pbuf->pb_data;
102 		ip6 = (struct ip6_hdr *)NULL;
103 	} else {
104 		ip = (struct ip *)NULL;
105 		ip6 = pbuf->pb_data;
106 	}
107 	if (!pf_pull_hdr(pbuf, off, hdr, sizeof(hdr), tcp->th_off << 2, NULL, NULL,
108 	        pd->af)) {
109 		return NULL;
110 	}
111 
112 	return pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)(void *)hdr, sizeof(hdr));
113 }
114 
115 struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct ip * ip,const struct ip6_hdr * ip6,const struct tcphdr * __sized_by (tcphdr_max_len)tcp,size_t tcphdr_max_len)116 pf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6,
117     const struct tcphdr *__sized_by(tcphdr_max_len)tcp, size_t tcphdr_max_len)
118 {
119 #pragma unused(tcphdr_max_len)
120 	struct pf_os_fingerprint fp, *__single fpresult;
121 	char srcname[128];
122 	uint8_t const *tcphdr = (uint8_t const *__bidi_indexable)tcp;
123 	uint8_t const *tcpopt_ptr = tcphdr + sizeof(struct tcphdr);
124 	int tcpopt_cnt = (tcp->th_off << 2) + sizeof(struct tcphdr);
125 	int optlen = 0;
126 
127 	if ((tcp->th_flags & (TH_SYN | TH_ACK)) != TH_SYN) {
128 		return NULL;
129 	}
130 	if (ip) {
131 		if ((ip->ip_off & htons(IP_OFFMASK)) != 0) {
132 			return NULL;
133 		}
134 	}
135 
136 	memset(&fp, 0, sizeof(fp));
137 
138 	if (ip) {
139 		fp.fp_psize = ntohs(ip->ip_len);
140 		fp.fp_ttl = ip->ip_ttl;
141 		if (ip->ip_off & htons(IP_DF)) {
142 			fp.fp_flags |= PF_OSFP_DF;
143 		}
144 		(void) inet_ntop(AF_INET, &ip->ip_src, srcname,
145 		    (socklen_t)sizeof(srcname));
146 	} else if (ip6) {
147 		/* jumbo payload? */
148 		fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
149 		fp.fp_ttl = ip6->ip6_hlim;
150 		fp.fp_flags |= PF_OSFP_DF;
151 		fp.fp_flags |= PF_OSFP_INET6;
152 		(void) inet_ntop(AF_INET6, &ip6->ip6_src, srcname,
153 		    (socklen_t)sizeof(srcname));
154 	} else {
155 		return NULL;
156 	}
157 	fp.fp_wsize = ntohs(tcp->th_win);
158 
159 	for (; tcpopt_cnt > 0; tcpopt_cnt -= optlen, tcpopt_ptr += optlen) {
160 		if (*tcpopt_ptr == TCPOPT_EOL) {
161 			break;
162 		}
163 
164 		fp.fp_optcnt++;
165 		if (*tcpopt_ptr == TCPOPT_NOP) {
166 			fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
167 			    PF_OSFP_TCPOPT_NOP;
168 			optlen = 1;
169 		} else {
170 			if (tcpopt_cnt < 2) {
171 				return NULL;
172 			}
173 			optlen = tcpopt_ptr[1];
174 			if (optlen > tcpopt_cnt || optlen < 2) {
175 				return NULL;
176 			}
177 			switch (*tcpopt_ptr) {
178 			case TCPOPT_MAXSEG:
179 				if (optlen >= TCPOLEN_MAXSEG) {
180 					memcpy(&fp.fp_mss, &tcpopt_ptr[2],
181 					    sizeof(fp.fp_mss));
182 				}
183 				fp.fp_tcpopts = (fp.fp_tcpopts <<
184 				        PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
185 #if BYTE_ORDER != BIG_ENDIAN
186 				NTOHS(fp.fp_mss);
187 #endif
188 				break;
189 			case TCPOPT_WINDOW:
190 				if (optlen >= TCPOLEN_WINDOW) {
191 					memcpy(&fp.fp_wscale, &tcpopt_ptr[2],
192 					    sizeof(fp.fp_wscale));
193 				}
194 				fp.fp_tcpopts = (fp.fp_tcpopts <<
195 				        PF_OSFP_TCPOPT_BITS) |
196 				    PF_OSFP_TCPOPT_WSCALE;
197 				break;
198 			case TCPOPT_SACK_PERMITTED:
199 				fp.fp_tcpopts = (fp.fp_tcpopts <<
200 				        PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
201 				break;
202 			case TCPOPT_TIMESTAMP:
203 				if (optlen >= TCPOLEN_TIMESTAMP) {
204 					u_int32_t ts;
205 					memcpy(&ts, &tcpopt_ptr[2], sizeof(ts));
206 					if (ts == 0) {
207 						fp.fp_flags |= PF_OSFP_TS0;
208 					}
209 				}
210 				fp.fp_tcpopts = (fp.fp_tcpopts <<
211 				        PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
212 				break;
213 			default:
214 				return NULL;
215 			}
216 		}
217 		optlen = MAX(optlen, 1);        /* paranoia */
218 	}
219 
220 	DPFPRINTF("fingerprinted %s:%d  %d:%d:%d:%d:%llx (%d) "
221 	    "(TS=%s,M=%s%d,W=%s%d)\n",
222 	    srcname, ntohs(tcp->th_sport),
223 	    fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
224 	    fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
225 	    (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
226 	    (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
227 	    (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
228 	    fp.fp_mss,
229 	    (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
230 	    (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
231 	    fp.fp_wscale);
232 
233 	if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
234 	    PF_OSFP_MAXTTL_OFFSET))) {
235 		return &fpresult->fp_oses;
236 	}
237 	return NULL;
238 }
239 
240 /* Match a fingerprint ID against a list of OSes */
241 int
pf_osfp_match(struct pf_osfp_enlist * list,pf_osfp_t os)242 pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
243 {
244 	struct pf_osfp_entry *entry;
245 	int os_class, os_version, os_subtype;
246 	int en_class, en_version, en_subtype;
247 
248 	if (os == PF_OSFP_ANY) {
249 		return 1;
250 	}
251 	if (list == NULL) {
252 		DPFPRINTF("osfp no match against %x\n", os);
253 		return os == PF_OSFP_UNKNOWN;
254 	}
255 	PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
256 	SLIST_FOREACH(entry, list, fp_entry) {
257 		PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
258 		if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
259 		    (os_version == PF_OSFP_ANY || en_version == os_version) &&
260 		    (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
261 			DPFPRINTF("osfp matched %s %s %s  %x==%x\n",
262 			    entry->fp_class_nm, entry->fp_version_nm,
263 			    entry->fp_subtype_nm, os, entry->fp_os);
264 			return 1;
265 		}
266 	}
267 	DPFPRINTF("fingerprint 0x%x didn't match\n", os);
268 	return 0;
269 }
270 
271 /* Initialize the OS fingerprint system */
272 void
pf_osfp_initialize(void)273 pf_osfp_initialize(void)
274 {
275 	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
276 	    "pfosfpen", NULL);
277 	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
278 	    "pfosfp", NULL);
279 	SLIST_INIT(&pf_osfp_list);
280 }
281 
282 #if 0
283 void
284 pf_osfp_destroy(void)
285 {
286 	pf_osfp_flush();
287 
288 	pool_destroy(&pf_osfp_pl);
289 	pool_destroy(&pf_osfp_entry_pl);
290 }
291 #endif
292 
293 /* Flush the fingerprint list */
294 void
pf_osfp_flush(void)295 pf_osfp_flush(void)
296 {
297 	struct pf_os_fingerprint *fp;
298 	struct pf_osfp_entry *entry;
299 
300 	while ((fp = SLIST_FIRST(&pf_osfp_list))) {
301 		SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
302 		while ((entry = SLIST_FIRST(&fp->fp_oses))) {
303 			SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
304 			pool_put(&pf_osfp_entry_pl, entry);
305 		}
306 		pool_put(&pf_osfp_pl, fp);
307 	}
308 }
309 
310 
311 /* Add a fingerprint */
312 int
pf_osfp_add(struct pf_osfp_ioctl * fpioc)313 pf_osfp_add(struct pf_osfp_ioctl *fpioc)
314 {
315 	struct pf_os_fingerprint *__single fp, fpadd;
316 	struct pf_osfp_entry *__single entry, *__single uentry;
317 
318 	memset(&fpadd, 0, sizeof(fpadd));
319 	fpadd.fp_tcpopts = fpioc->fp_tcpopts;
320 	fpadd.fp_wsize = fpioc->fp_wsize;
321 	fpadd.fp_psize = fpioc->fp_psize;
322 	fpadd.fp_mss = fpioc->fp_mss;
323 	fpadd.fp_flags = fpioc->fp_flags;
324 	fpadd.fp_optcnt = fpioc->fp_optcnt;
325 	fpadd.fp_wscale = fpioc->fp_wscale;
326 	fpadd.fp_ttl = fpioc->fp_ttl;
327 
328 	uentry = &fpioc->fp_os;
329 	uentry->fp_entry.sle_next = NULL;
330 	uentry->fp_class_nm[sizeof(uentry->fp_class_nm) - 1] = '\0';
331 	uentry->fp_version_nm[sizeof(uentry->fp_version_nm) - 1] = '\0';
332 	uentry->fp_subtype_nm[sizeof(uentry->fp_subtype_nm) - 1] = '\0';
333 
334 	DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
335 	    "(TS=%s,M=%s%d,W=%s%d) %x\n",
336 	    fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
337 	    fpioc->fp_os.fp_subtype_nm,
338 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
339 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
340 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
341 	    (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
342 	    fpadd.fp_wsize,
343 	    fpadd.fp_ttl,
344 	    (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
345 	    (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
346 	    (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
347 	    fpadd.fp_psize,
348 	    (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
349 	    (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
350 	    (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
351 	    (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
352 	    fpadd.fp_mss,
353 	    (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
354 	    (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
355 	    fpadd.fp_wscale,
356 	    fpioc->fp_os.fp_os);
357 
358 
359 	if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
360 		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
361 			if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os)) {
362 				return EEXIST;
363 			}
364 		}
365 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_WAITOK)) == NULL) {
366 			return ENOMEM;
367 		}
368 	} else {
369 		if ((fp = pool_get(&pf_osfp_pl, PR_WAITOK)) == NULL) {
370 			return ENOMEM;
371 		}
372 		memset(fp, 0, sizeof(*fp));
373 		fp->fp_tcpopts = fpioc->fp_tcpopts;
374 		fp->fp_wsize = fpioc->fp_wsize;
375 		fp->fp_psize = fpioc->fp_psize;
376 		fp->fp_mss = fpioc->fp_mss;
377 		fp->fp_flags = fpioc->fp_flags;
378 		fp->fp_optcnt = fpioc->fp_optcnt;
379 		fp->fp_wscale = fpioc->fp_wscale;
380 		fp->fp_ttl = fpioc->fp_ttl;
381 		SLIST_INIT(&fp->fp_oses);
382 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_WAITOK)) == NULL) {
383 			pool_put(&pf_osfp_pl, fp);
384 			return ENOMEM;
385 		}
386 		pf_osfp_insert(&pf_osfp_list, fp);
387 	}
388 	memcpy(entry, &fpioc->fp_os, sizeof(*entry));
389 
390 	/* Make sure the strings are NUL terminated */
391 	entry->fp_class_nm[sizeof(entry->fp_class_nm) - 1] = '\0';
392 	entry->fp_version_nm[sizeof(entry->fp_version_nm) - 1] = '\0';
393 	entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm) - 1] = '\0';
394 
395 	SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
396 
397 #ifdef PFDEBUG
398 	if ((fp = pf_osfp_validate())) {
399 		printf("Invalid fingerprint list\n");
400 	}
401 #endif /* PFDEBUG */
402 	return 0;
403 }
404 
405 
406 /* Find a fingerprint in the list */
407 struct pf_os_fingerprint *
pf_osfp_find(struct pf_osfp_list * list,struct pf_os_fingerprint * find,u_int8_t ttldiff)408 pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
409     u_int8_t ttldiff)
410 {
411 	struct pf_os_fingerprint *f;
412 
413 #define MATCH_INT(_MOD, _DC, _field)                                    \
414 	if ((f->fp_flags & _DC) == 0) {                                 \
415 	        if ((f->fp_flags & _MOD) == 0) {                        \
416 	                if (f->_field != find->_field)                  \
417 	                        continue;                               \
418 	        } else {                                                \
419 	                if (f->_field == 0 || find->_field % f->_field) \
420 	                        continue;                               \
421 	        }                                                       \
422 	}
423 
424 	SLIST_FOREACH(f, list, fp_next) {
425 		if (f->fp_tcpopts != find->fp_tcpopts ||
426 		    f->fp_optcnt != find->fp_optcnt ||
427 		    f->fp_ttl < find->fp_ttl ||
428 		    f->fp_ttl - find->fp_ttl > ttldiff ||
429 		    (f->fp_flags & (PF_OSFP_DF | PF_OSFP_TS0)) !=
430 		    (find->fp_flags & (PF_OSFP_DF | PF_OSFP_TS0))) {
431 			continue;
432 		}
433 
434 		MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
435 		MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
436 		MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
437 		if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
438 			if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
439 				if (find->fp_mss == 0) {
440 					continue;
441 				}
442 
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 				}
456 			} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
457 				if (find->fp_mss == 0) {
458 					continue;
459 				}
460 
461 #define MTUOFF  (sizeof (struct ip) + sizeof (struct tcphdr))
462 #define SMART_MTU       (SMART_MSS + MTUOFF)
463 				if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
464 				    find->fp_wsize / (find->fp_mss + MTUOFF) !=
465 				    f->fp_wsize) &&
466 				    (find->fp_wsize % SMART_MTU ||
467 				    find->fp_wsize / SMART_MTU !=
468 				    f->fp_wsize)) {
469 					continue;
470 				}
471 			} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
472 				if (f->fp_wsize == 0 || find->fp_wsize %
473 				    f->fp_wsize) {
474 					continue;
475 				}
476 			} else {
477 				if (f->fp_wsize != find->fp_wsize) {
478 					continue;
479 				}
480 			}
481 		}
482 		return f;
483 	}
484 
485 	return NULL;
486 }
487 
488 /* Find an exact fingerprint in the list */
489 struct pf_os_fingerprint *
pf_osfp_find_exact(struct pf_osfp_list * list,struct pf_os_fingerprint * find)490 pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
491 {
492 	struct pf_os_fingerprint *f;
493 
494 	SLIST_FOREACH(f, list, fp_next) {
495 		if (f->fp_tcpopts == find->fp_tcpopts &&
496 		    f->fp_wsize == find->fp_wsize &&
497 		    f->fp_psize == find->fp_psize &&
498 		    f->fp_mss == find->fp_mss &&
499 		    f->fp_flags == find->fp_flags &&
500 		    f->fp_optcnt == find->fp_optcnt &&
501 		    f->fp_wscale == find->fp_wscale &&
502 		    f->fp_ttl == find->fp_ttl) {
503 			return f;
504 		}
505 	}
506 
507 	return NULL;
508 }
509 
510 /* Insert a fingerprint into the list */
511 void
pf_osfp_insert(struct pf_osfp_list * list,struct pf_os_fingerprint * ins)512 pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
513 {
514 	struct pf_os_fingerprint *f, *prev = NULL;
515 
516 	/* XXX need to go semi tree based.  can key on tcp options */
517 
518 	SLIST_FOREACH(f, list, fp_next)
519 	prev = f;
520 	if (prev) {
521 		SLIST_INSERT_AFTER(prev, ins, fp_next);
522 	} else {
523 		SLIST_INSERT_HEAD(list, ins, fp_next);
524 	}
525 }
526 
527 /* Fill a fingerprint by its number (from an ioctl) */
528 int
pf_osfp_get(struct pf_osfp_ioctl * fpioc)529 pf_osfp_get(struct pf_osfp_ioctl *fpioc)
530 {
531 	struct pf_os_fingerprint *fp;
532 	struct pf_osfp_entry *entry;
533 	int num = fpioc->fp_getnum;
534 	int i = 0;
535 
536 
537 	memset(fpioc, 0, sizeof(*fpioc));
538 	SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
539 		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
540 			if (i++ == num) {
541 				fpioc->fp_mss = fp->fp_mss;
542 				fpioc->fp_wsize = fp->fp_wsize;
543 				fpioc->fp_flags = fp->fp_flags;
544 				fpioc->fp_psize = fp->fp_psize;
545 				fpioc->fp_ttl = fp->fp_ttl;
546 				fpioc->fp_wscale = fp->fp_wscale;
547 				fpioc->fp_getnum = num;
548 				memcpy(&fpioc->fp_os, entry,
549 				    sizeof(fpioc->fp_os));
550 				fpioc->fp_os.fp_entry.sle_next = NULL;
551 				return 0;
552 			}
553 		}
554 	}
555 
556 	return EBUSY;
557 }
558 
559 
560 /* Validate that each signature is reachable */
561 struct pf_os_fingerprint *
pf_osfp_validate(void)562 pf_osfp_validate(void)
563 {
564 	struct pf_os_fingerprint *f, *f2, find;
565 
566 	SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
567 		memcpy(&find, f, sizeof(find));
568 
569 		/* We do a few MSS/th_win percolations to make things unique */
570 		if (find.fp_mss == 0) {
571 			find.fp_mss = 128;
572 		}
573 		if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
574 			find.fp_wsize *= find.fp_mss;
575 		} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
576 			find.fp_wsize *= (find.fp_mss + 40);
577 		} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
578 			find.fp_wsize *= 2;
579 		}
580 		if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
581 			if (f2) {
582 				printf("Found \"%s %s %s\" instead of "
583 				    "\"%s %s %s\"\n",
584 				    SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
585 				    SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
586 				    SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
587 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
588 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
589 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
590 			} else {
591 				printf("Couldn't find \"%s %s %s\"\n",
592 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
593 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
594 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
595 			}
596 			return f;
597 		}
598 	}
599 	return NULL;
600 }
601