pf_osfp.c revision 315026
1244769Sglebius/*-
2126258Smlaier * Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
3126258Smlaier *
4126258Smlaier * Permission to use, copy, modify, and distribute this software for any
5126258Smlaier * purpose with or without fee is hereby granted, provided that the above
6126258Smlaier * copyright notice and this permission notice appear in all copies.
7126258Smlaier *
8126258Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9126258Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10126258Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11126258Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12126258Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13126258Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14126258Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15126258Smlaier *
16244769Sglebius *	$OpenBSD: pf_osfp.c,v 1.14 2008/06/12 18:17:01 henning Exp $
17126258Smlaier */
18126258Smlaier
19171168Smlaier#include <sys/cdefs.h>
20171168Smlaier__FBSDID("$FreeBSD: stable/11/sys/netpfil/pf/pf_osfp.c 315026 2017-03-10 20:25:43Z vangyzen $");
21171168Smlaier
22315026Svangyzen#include "opt_inet6.h"
23315026Svangyzen
24126258Smlaier#include <sys/param.h>
25240233Sglebius#include <sys/kernel.h>
26257176Sglebius#include <sys/lock.h>
27257176Sglebius#include <sys/mbuf.h>
28257176Sglebius#include <sys/rwlock.h>
29126258Smlaier#include <sys/socket.h>
30126258Smlaier
31126258Smlaier#include <netinet/in.h>
32126258Smlaier#include <netinet/ip.h>
33126258Smlaier#include <netinet/tcp.h>
34126258Smlaier
35126258Smlaier#include <net/if.h>
36257176Sglebius#include <net/vnet.h>
37126258Smlaier#include <net/pfvar.h>
38126258Smlaier
39315026Svangyzen#ifdef INET6
40126258Smlaier#include <netinet/ip6.h>
41315026Svangyzen#endif
42126258Smlaier
43240233Sglebiusstatic MALLOC_DEFINE(M_PFOSFP, "pf_osfp", "pf(4) operating system fingerprints");
44223637Sbz#define	DPFPRINTF(format, x...)		\
45223637Sbz	if (V_pf_status.debug >= PF_DEBUG_NOISY)	\
46223637Sbz		printf(format , ##x)
47126258Smlaier
48223637SbzSLIST_HEAD(pf_osfp_list, pf_os_fingerprint);
49240233Sglebiusstatic VNET_DEFINE(struct pf_osfp_list,	pf_osfp_list) =
50240233Sglebius	SLIST_HEAD_INITIALIZER();
51223637Sbz#define	V_pf_osfp_list			VNET(pf_osfp_list)
52126258Smlaier
53240233Sglebiusstatic struct pf_osfp_enlist	*pf_osfp_fingerprint_hdr(const struct ip *,
54240233Sglebius				    const struct ip6_hdr *,
55240233Sglebius				    const struct tcphdr *);
56240233Sglebiusstatic struct pf_os_fingerprint	*pf_osfp_find(struct pf_osfp_list *,
57126258Smlaier				    struct pf_os_fingerprint *, u_int8_t);
58240233Sglebiusstatic struct pf_os_fingerprint	*pf_osfp_find_exact(struct pf_osfp_list *,
59126258Smlaier				    struct pf_os_fingerprint *);
60240233Sglebiusstatic void			 pf_osfp_insert(struct pf_osfp_list *,
61126258Smlaier				    struct pf_os_fingerprint *);
62240233Sglebius#ifdef PFDEBUG
63240233Sglebiusstatic struct pf_os_fingerprint	*pf_osfp_validate(void);
64240233Sglebius#endif
65126258Smlaier
66126258Smlaier/*
67126258Smlaier * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
68126258Smlaier * Returns the list of possible OSes.
69126258Smlaier */
70126258Smlaierstruct pf_osfp_enlist *
71126258Smlaierpf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off,
72126258Smlaier    const struct tcphdr *tcp)
73126258Smlaier{
74126258Smlaier	struct ip *ip;
75171168Smlaier	struct ip6_hdr *ip6;
76126258Smlaier	char hdr[60];
77126258Smlaier
78171168Smlaier	if ((pd->af != PF_INET && pd->af != PF_INET6) ||
79171168Smlaier	    pd->proto != IPPROTO_TCP || (tcp->th_off << 2) < sizeof(*tcp))
80126258Smlaier		return (NULL);
81126258Smlaier
82171168Smlaier	if (pd->af == PF_INET) {
83171168Smlaier		ip = mtod(m, struct ip *);
84171168Smlaier		ip6 = (struct ip6_hdr *)NULL;
85171168Smlaier	} else {
86171168Smlaier		ip = (struct ip *)NULL;
87171168Smlaier		ip6 = mtod(m, struct ip6_hdr *);
88171168Smlaier	}
89171168Smlaier	if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL,
90171168Smlaier	    pd->af)) return (NULL);
91126258Smlaier
92171168Smlaier	return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr));
93126258Smlaier}
94126258Smlaier
95240233Sglebiusstatic struct pf_osfp_enlist *
96171168Smlaierpf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp)
97126258Smlaier{
98126258Smlaier	struct pf_os_fingerprint fp, *fpresult;
99126258Smlaier	int cnt, optlen = 0;
100126261Smlaier	const u_int8_t *optp;
101315026Svangyzen#ifdef INET6
102315026Svangyzen	char srcname[INET6_ADDRSTRLEN];
103315026Svangyzen#else
104315026Svangyzen	char srcname[INET_ADDRSTRLEN];
105315026Svangyzen#endif
106126258Smlaier
107171168Smlaier	if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN)
108126258Smlaier		return (NULL);
109171168Smlaier	if (ip) {
110171168Smlaier		if ((ip->ip_off & htons(IP_OFFMASK)) != 0)
111171168Smlaier			return (NULL);
112171168Smlaier	}
113126258Smlaier
114126258Smlaier	memset(&fp, 0, sizeof(fp));
115126258Smlaier
116171168Smlaier	if (ip) {
117171168Smlaier		fp.fp_psize = ntohs(ip->ip_len);
118171168Smlaier		fp.fp_ttl = ip->ip_ttl;
119171168Smlaier		if (ip->ip_off & htons(IP_DF))
120171168Smlaier			fp.fp_flags |= PF_OSFP_DF;
121315026Svangyzen		inet_ntoa_r(ip->ip_src, srcname);
122171168Smlaier	}
123171168Smlaier#ifdef INET6
124171168Smlaier	else if (ip6) {
125171168Smlaier		/* jumbo payload? */
126171168Smlaier		fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
127171168Smlaier		fp.fp_ttl = ip6->ip6_hlim;
128126258Smlaier		fp.fp_flags |= PF_OSFP_DF;
129171168Smlaier		fp.fp_flags |= PF_OSFP_INET6;
130315026Svangyzen		ip6_sprintf(srcname, (const struct in6_addr *)&ip6->ip6_src);
131171168Smlaier	}
132171168Smlaier#endif
133171168Smlaier	else
134171168Smlaier		return (NULL);
135126258Smlaier	fp.fp_wsize = ntohs(tcp->th_win);
136126258Smlaier
137126258Smlaier
138126258Smlaier	cnt = (tcp->th_off << 2) - sizeof(*tcp);
139126261Smlaier	optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
140126258Smlaier	for (; cnt > 0; cnt -= optlen, optp += optlen) {
141126258Smlaier		if (*optp == TCPOPT_EOL)
142126258Smlaier			break;
143126258Smlaier
144126258Smlaier		fp.fp_optcnt++;
145126258Smlaier		if (*optp == TCPOPT_NOP) {
146126258Smlaier			fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
147126258Smlaier			    PF_OSFP_TCPOPT_NOP;
148126258Smlaier			optlen = 1;
149126258Smlaier		} else {
150126258Smlaier			if (cnt < 2)
151126258Smlaier				return (NULL);
152126258Smlaier			optlen = optp[1];
153126258Smlaier			if (optlen > cnt || optlen < 2)
154126258Smlaier				return (NULL);
155126258Smlaier			switch (*optp) {
156126258Smlaier			case TCPOPT_MAXSEG:
157126258Smlaier				if (optlen >= TCPOLEN_MAXSEG)
158126258Smlaier					memcpy(&fp.fp_mss, &optp[2],
159126258Smlaier					    sizeof(fp.fp_mss));
160126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
161126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
162126258Smlaier				NTOHS(fp.fp_mss);
163126258Smlaier				break;
164126258Smlaier			case TCPOPT_WINDOW:
165126258Smlaier				if (optlen >= TCPOLEN_WINDOW)
166126258Smlaier					memcpy(&fp.fp_wscale, &optp[2],
167126258Smlaier					    sizeof(fp.fp_wscale));
168126258Smlaier				NTOHS(fp.fp_wscale);
169126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
170126258Smlaier				    PF_OSFP_TCPOPT_BITS) |
171126258Smlaier				    PF_OSFP_TCPOPT_WSCALE;
172126258Smlaier				break;
173126258Smlaier			case TCPOPT_SACK_PERMITTED:
174126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
175126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
176126258Smlaier				break;
177126258Smlaier			case TCPOPT_TIMESTAMP:
178126258Smlaier				if (optlen >= TCPOLEN_TIMESTAMP) {
179126258Smlaier					u_int32_t ts;
180126258Smlaier					memcpy(&ts, &optp[2], sizeof(ts));
181126258Smlaier					if (ts == 0)
182126258Smlaier						fp.fp_flags |= PF_OSFP_TS0;
183126258Smlaier
184126258Smlaier				}
185126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
186126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
187126258Smlaier				break;
188126258Smlaier			default:
189126258Smlaier				return (NULL);
190126258Smlaier			}
191126258Smlaier		}
192126258Smlaier		optlen = MAX(optlen, 1);	/* paranoia */
193126258Smlaier	}
194126258Smlaier
195126258Smlaier	DPFPRINTF("fingerprinted %s:%d  %d:%d:%d:%d:%llx (%d) "
196126258Smlaier	    "(TS=%s,M=%s%d,W=%s%d)\n",
197171168Smlaier	    srcname, ntohs(tcp->th_sport),
198126258Smlaier	    fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
199126258Smlaier	    fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
200126258Smlaier	    (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
201126258Smlaier	    (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
202126258Smlaier	    (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
203126258Smlaier	    fp.fp_mss,
204126258Smlaier	    (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
205126258Smlaier	    (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
206126258Smlaier	    fp.fp_wscale);
207126258Smlaier
208223637Sbz	if ((fpresult = pf_osfp_find(&V_pf_osfp_list, &fp,
209126258Smlaier	    PF_OSFP_MAXTTL_OFFSET)))
210126258Smlaier		return (&fpresult->fp_oses);
211126258Smlaier	return (NULL);
212126258Smlaier}
213126258Smlaier
214126258Smlaier/* Match a fingerprint ID against a list of OSes */
215126258Smlaierint
216126258Smlaierpf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
217126258Smlaier{
218126258Smlaier	struct pf_osfp_entry *entry;
219126258Smlaier	int os_class, os_version, os_subtype;
220126258Smlaier	int en_class, en_version, en_subtype;
221126258Smlaier
222126258Smlaier	if (os == PF_OSFP_ANY)
223126258Smlaier		return (1);
224126258Smlaier	if (list == NULL) {
225126258Smlaier		DPFPRINTF("osfp no match against %x\n", os);
226126258Smlaier		return (os == PF_OSFP_UNKNOWN);
227126258Smlaier	}
228126258Smlaier	PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
229126258Smlaier	SLIST_FOREACH(entry, list, fp_entry) {
230126258Smlaier		PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
231126258Smlaier		if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
232126258Smlaier		    (os_version == PF_OSFP_ANY || en_version == os_version) &&
233126258Smlaier		    (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
234126258Smlaier			DPFPRINTF("osfp matched %s %s %s  %x==%x\n",
235126258Smlaier			    entry->fp_class_nm, entry->fp_version_nm,
236126258Smlaier			    entry->fp_subtype_nm, os, entry->fp_os);
237126258Smlaier			return (1);
238126258Smlaier		}
239126258Smlaier	}
240126258Smlaier	DPFPRINTF("fingerprint 0x%x didn't match\n", os);
241126258Smlaier	return (0);
242126258Smlaier}
243126258Smlaier
244126258Smlaier/* Flush the fingerprint list */
245126258Smlaiervoid
246126258Smlaierpf_osfp_flush(void)
247126258Smlaier{
248126258Smlaier	struct pf_os_fingerprint *fp;
249126258Smlaier	struct pf_osfp_entry *entry;
250126258Smlaier
251223637Sbz	while ((fp = SLIST_FIRST(&V_pf_osfp_list))) {
252223637Sbz		SLIST_REMOVE_HEAD(&V_pf_osfp_list, fp_next);
253126258Smlaier		while ((entry = SLIST_FIRST(&fp->fp_oses))) {
254126258Smlaier			SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
255240233Sglebius			free(entry, M_PFOSFP);
256126258Smlaier		}
257240233Sglebius		free(fp, M_PFOSFP);
258126258Smlaier	}
259126258Smlaier}
260126258Smlaier
261126258Smlaier
262126258Smlaier/* Add a fingerprint */
263126258Smlaierint
264126258Smlaierpf_osfp_add(struct pf_osfp_ioctl *fpioc)
265126258Smlaier{
266126258Smlaier	struct pf_os_fingerprint *fp, fpadd;
267126258Smlaier	struct pf_osfp_entry *entry;
268126258Smlaier
269240233Sglebius	PF_RULES_WASSERT();
270240233Sglebius
271126258Smlaier	memset(&fpadd, 0, sizeof(fpadd));
272126258Smlaier	fpadd.fp_tcpopts = fpioc->fp_tcpopts;
273126258Smlaier	fpadd.fp_wsize = fpioc->fp_wsize;
274126258Smlaier	fpadd.fp_psize = fpioc->fp_psize;
275126258Smlaier	fpadd.fp_mss = fpioc->fp_mss;
276126258Smlaier	fpadd.fp_flags = fpioc->fp_flags;
277126258Smlaier	fpadd.fp_optcnt = fpioc->fp_optcnt;
278126258Smlaier	fpadd.fp_wscale = fpioc->fp_wscale;
279126258Smlaier	fpadd.fp_ttl = fpioc->fp_ttl;
280126258Smlaier
281223637Sbz#if 0	/* XXX RYAN wants to fix logging */
282126258Smlaier	DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
283126258Smlaier	    "(TS=%s,M=%s%d,W=%s%d) %x\n",
284126258Smlaier	    fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
285126258Smlaier	    fpioc->fp_os.fp_subtype_nm,
286126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
287126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
288126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
289126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
290126258Smlaier	    fpadd.fp_wsize,
291126258Smlaier	    fpadd.fp_ttl,
292126258Smlaier	    (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
293126258Smlaier	    (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
294126258Smlaier	    (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
295126258Smlaier	    fpadd.fp_psize,
296126258Smlaier	    (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
297126258Smlaier	    (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
298126258Smlaier	    (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
299126258Smlaier	    (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
300126258Smlaier	    fpadd.fp_mss,
301126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
302126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
303126258Smlaier	    fpadd.fp_wscale,
304126258Smlaier	    fpioc->fp_os.fp_os);
305223637Sbz#endif
306126258Smlaier
307223637Sbz	if ((fp = pf_osfp_find_exact(&V_pf_osfp_list, &fpadd))) {
308126258Smlaier		 SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
309126258Smlaier			if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
310126258Smlaier				return (EEXIST);
311126258Smlaier		}
312240233Sglebius		if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT))
313240233Sglebius		    == NULL)
314126258Smlaier			return (ENOMEM);
315126258Smlaier	} else {
316240233Sglebius		if ((fp = malloc(sizeof(*fp), M_PFOSFP, M_ZERO | M_NOWAIT))
317240233Sglebius		    == NULL)
318126258Smlaier			return (ENOMEM);
319126258Smlaier		fp->fp_tcpopts = fpioc->fp_tcpopts;
320126258Smlaier		fp->fp_wsize = fpioc->fp_wsize;
321126258Smlaier		fp->fp_psize = fpioc->fp_psize;
322126258Smlaier		fp->fp_mss = fpioc->fp_mss;
323126258Smlaier		fp->fp_flags = fpioc->fp_flags;
324126258Smlaier		fp->fp_optcnt = fpioc->fp_optcnt;
325126258Smlaier		fp->fp_wscale = fpioc->fp_wscale;
326126258Smlaier		fp->fp_ttl = fpioc->fp_ttl;
327126258Smlaier		SLIST_INIT(&fp->fp_oses);
328240233Sglebius		if ((entry = malloc(sizeof(*entry), M_PFOSFP, M_NOWAIT))
329240233Sglebius		    == NULL) {
330240233Sglebius			free(fp, M_PFOSFP);
331126258Smlaier			return (ENOMEM);
332126258Smlaier		}
333223637Sbz		pf_osfp_insert(&V_pf_osfp_list, fp);
334126258Smlaier	}
335126258Smlaier	memcpy(entry, &fpioc->fp_os, sizeof(*entry));
336126258Smlaier
337126258Smlaier	/* Make sure the strings are NUL terminated */
338126258Smlaier	entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
339126258Smlaier	entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
340126258Smlaier	entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
341126258Smlaier
342126258Smlaier	SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
343126258Smlaier
344126258Smlaier#ifdef PFDEBUG
345126258Smlaier	if ((fp = pf_osfp_validate()))
346126258Smlaier		printf("Invalid fingerprint list\n");
347126258Smlaier#endif /* PFDEBUG */
348126258Smlaier	return (0);
349126258Smlaier}
350126258Smlaier
351126258Smlaier
352126258Smlaier/* Find a fingerprint in the list */
353240233Sglebiusstatic struct pf_os_fingerprint *
354126258Smlaierpf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
355126258Smlaier    u_int8_t ttldiff)
356126258Smlaier{
357126258Smlaier	struct pf_os_fingerprint *f;
358126258Smlaier
359223637Sbz#define	MATCH_INT(_MOD, _DC, _field)					\
360126258Smlaier	if ((f->fp_flags & _DC) == 0) {					\
361126258Smlaier		if ((f->fp_flags & _MOD) == 0) {			\
362126258Smlaier			if (f->_field != find->_field)			\
363126258Smlaier				continue;				\
364126258Smlaier		} else {						\
365126258Smlaier			if (f->_field == 0 || find->_field % f->_field)	\
366126258Smlaier				continue;				\
367126258Smlaier		}							\
368126258Smlaier	}
369126258Smlaier
370126258Smlaier	SLIST_FOREACH(f, list, fp_next) {
371126258Smlaier		if (f->fp_tcpopts != find->fp_tcpopts ||
372126258Smlaier		    f->fp_optcnt != find->fp_optcnt ||
373126258Smlaier		    f->fp_ttl < find->fp_ttl ||
374126258Smlaier		    f->fp_ttl - find->fp_ttl > ttldiff ||
375126258Smlaier		    (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
376126258Smlaier		    (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
377126258Smlaier			continue;
378126258Smlaier
379126258Smlaier		MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
380126258Smlaier		MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
381126258Smlaier		MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
382126258Smlaier		if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
383126258Smlaier			if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
384126258Smlaier				if (find->fp_mss == 0)
385126258Smlaier					continue;
386126258Smlaier
387223637Sbz/*
388223637Sbz * Some "smart" NAT devices and DSL routers will tweak the MSS size and
389126258Smlaier * will set it to whatever is suitable for the link type.
390126258Smlaier */
391223637Sbz#define	SMART_MSS	1460
392126258Smlaier				if ((find->fp_wsize % find->fp_mss ||
393126258Smlaier				    find->fp_wsize / find->fp_mss !=
394126258Smlaier				    f->fp_wsize) &&
395126258Smlaier				    (find->fp_wsize % SMART_MSS ||
396126258Smlaier				    find->fp_wsize / SMART_MSS !=
397126258Smlaier				    f->fp_wsize))
398126258Smlaier					continue;
399126258Smlaier			} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
400126258Smlaier				if (find->fp_mss == 0)
401126258Smlaier					continue;
402126258Smlaier
403223637Sbz#define	MTUOFF		(sizeof(struct ip) + sizeof(struct tcphdr))
404223637Sbz#define	SMART_MTU	(SMART_MSS + MTUOFF)
405126258Smlaier				if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
406126258Smlaier				    find->fp_wsize / (find->fp_mss + MTUOFF) !=
407126258Smlaier				    f->fp_wsize) &&
408126258Smlaier				    (find->fp_wsize % SMART_MTU ||
409126258Smlaier				    find->fp_wsize / SMART_MTU !=
410126258Smlaier				    f->fp_wsize))
411126258Smlaier					continue;
412126258Smlaier			} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
413126258Smlaier				if (f->fp_wsize == 0 || find->fp_wsize %
414126258Smlaier				    f->fp_wsize)
415126258Smlaier					continue;
416126258Smlaier			} else {
417126258Smlaier				if (f->fp_wsize != find->fp_wsize)
418126258Smlaier					continue;
419126258Smlaier			}
420126258Smlaier		}
421126258Smlaier		return (f);
422126258Smlaier	}
423126258Smlaier
424126258Smlaier	return (NULL);
425126258Smlaier}
426126258Smlaier
427126258Smlaier/* Find an exact fingerprint in the list */
428240233Sglebiusstatic struct pf_os_fingerprint *
429126258Smlaierpf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
430126258Smlaier{
431126258Smlaier	struct pf_os_fingerprint *f;
432126258Smlaier
433126258Smlaier	SLIST_FOREACH(f, list, fp_next) {
434126258Smlaier		if (f->fp_tcpopts == find->fp_tcpopts &&
435126258Smlaier		    f->fp_wsize == find->fp_wsize &&
436126258Smlaier		    f->fp_psize == find->fp_psize &&
437126258Smlaier		    f->fp_mss == find->fp_mss &&
438126258Smlaier		    f->fp_flags == find->fp_flags &&
439126258Smlaier		    f->fp_optcnt == find->fp_optcnt &&
440126258Smlaier		    f->fp_wscale == find->fp_wscale &&
441126258Smlaier		    f->fp_ttl == find->fp_ttl)
442126258Smlaier			return (f);
443126258Smlaier	}
444126258Smlaier
445126258Smlaier	return (NULL);
446126258Smlaier}
447126258Smlaier
448126258Smlaier/* Insert a fingerprint into the list */
449240233Sglebiusstatic void
450126258Smlaierpf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
451126258Smlaier{
452126258Smlaier	struct pf_os_fingerprint *f, *prev = NULL;
453126258Smlaier
454126258Smlaier	/* XXX need to go semi tree based.  can key on tcp options */
455126258Smlaier
456126258Smlaier	SLIST_FOREACH(f, list, fp_next)
457126258Smlaier		prev = f;
458126258Smlaier	if (prev)
459126258Smlaier		SLIST_INSERT_AFTER(prev, ins, fp_next);
460126258Smlaier	else
461126258Smlaier		SLIST_INSERT_HEAD(list, ins, fp_next);
462126258Smlaier}
463126258Smlaier
464126258Smlaier/* Fill a fingerprint by its number (from an ioctl) */
465126258Smlaierint
466126258Smlaierpf_osfp_get(struct pf_osfp_ioctl *fpioc)
467126258Smlaier{
468126258Smlaier	struct pf_os_fingerprint *fp;
469126258Smlaier	struct pf_osfp_entry *entry;
470126258Smlaier	int num = fpioc->fp_getnum;
471126258Smlaier	int i = 0;
472126258Smlaier
473126258Smlaier
474126258Smlaier	memset(fpioc, 0, sizeof(*fpioc));
475223637Sbz	SLIST_FOREACH(fp, &V_pf_osfp_list, fp_next) {
476126258Smlaier		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
477126258Smlaier			if (i++ == num) {
478126258Smlaier				fpioc->fp_mss = fp->fp_mss;
479126258Smlaier				fpioc->fp_wsize = fp->fp_wsize;
480126258Smlaier				fpioc->fp_flags = fp->fp_flags;
481126258Smlaier				fpioc->fp_psize = fp->fp_psize;
482126258Smlaier				fpioc->fp_ttl = fp->fp_ttl;
483126258Smlaier				fpioc->fp_wscale = fp->fp_wscale;
484126258Smlaier				fpioc->fp_getnum = num;
485126258Smlaier				memcpy(&fpioc->fp_os, entry,
486126258Smlaier				    sizeof(fpioc->fp_os));
487126258Smlaier				return (0);
488126258Smlaier			}
489126258Smlaier		}
490126258Smlaier	}
491126258Smlaier
492126258Smlaier	return (EBUSY);
493126258Smlaier}
494126258Smlaier
495126258Smlaier
496240233Sglebius#ifdef PFDEBUG
497126258Smlaier/* Validate that each signature is reachable */
498240233Sglebiusstatic struct pf_os_fingerprint *
499126258Smlaierpf_osfp_validate(void)
500126258Smlaier{
501126258Smlaier	struct pf_os_fingerprint *f, *f2, find;
502126258Smlaier
503223637Sbz	SLIST_FOREACH(f, &V_pf_osfp_list, fp_next) {
504126258Smlaier		memcpy(&find, f, sizeof(find));
505126258Smlaier
506126258Smlaier		/* We do a few MSS/th_win percolations to make things unique */
507126258Smlaier		if (find.fp_mss == 0)
508126258Smlaier			find.fp_mss = 128;
509126258Smlaier		if (f->fp_flags & PF_OSFP_WSIZE_MSS)
510217388Scsjp			find.fp_wsize *= find.fp_mss;
511126258Smlaier		else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
512126258Smlaier			find.fp_wsize *= (find.fp_mss + 40);
513126258Smlaier		else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
514126258Smlaier			find.fp_wsize *= 2;
515223637Sbz		if (f != (f2 = pf_osfp_find(&V_pf_osfp_list, &find, 0))) {
516126258Smlaier			if (f2)
517126258Smlaier				printf("Found \"%s %s %s\" instead of "
518126258Smlaier				    "\"%s %s %s\"\n",
519126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
520126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
521126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
522126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
523126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
524126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
525126258Smlaier			else
526126258Smlaier				printf("Couldn't find \"%s %s %s\"\n",
527126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
528126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
529126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
530126258Smlaier			return (f);
531126258Smlaier		}
532126258Smlaier	}
533126258Smlaier	return (NULL);
534126258Smlaier}
535240233Sglebius#endif /* PFDEBUG */
536