pf_osfp.c revision 217388
1171168Smlaier/*	$OpenBSD: pf_osfp.c,v 1.12 2006/12/13 18:14:10 itojun Exp $ */
2126258Smlaier
3126258Smlaier/*
4126258Smlaier * Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org>
5126258Smlaier *
6126258Smlaier * Permission to use, copy, modify, and distribute this software for any
7126258Smlaier * purpose with or without fee is hereby granted, provided that the above
8126258Smlaier * copyright notice and this permission notice appear in all copies.
9126258Smlaier *
10126258Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11126258Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12126258Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13126258Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14126258Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15126258Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16126258Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17126258Smlaier *
18126258Smlaier */
19126258Smlaier
20171168Smlaier#ifdef __FreeBSD__
21171168Smlaier#include <sys/cdefs.h>
22171168Smlaier__FBSDID("$FreeBSD: head/sys/contrib/pf/net/pf_osfp.c 217388 2011-01-14 04:24:53Z csjp $");
23171168Smlaier#endif
24171168Smlaier
25126258Smlaier#include <sys/param.h>
26126258Smlaier#include <sys/socket.h>
27126258Smlaier#ifdef _KERNEL
28126258Smlaier# include <sys/systm.h>
29126258Smlaier#endif /* _KERNEL */
30126258Smlaier#include <sys/mbuf.h>
31126258Smlaier
32126258Smlaier#include <netinet/in.h>
33126258Smlaier#include <netinet/in_systm.h>
34126258Smlaier#include <netinet/ip.h>
35126258Smlaier#include <netinet/tcp.h>
36126258Smlaier
37126258Smlaier#include <net/if.h>
38126258Smlaier#include <net/pfvar.h>
39126258Smlaier
40126258Smlaier#include <netinet/ip6.h>
41171168Smlaier#ifdef _KERNEL
42171168Smlaier#include <netinet6/in6_var.h>
43171168Smlaier#endif
44126258Smlaier
45126258Smlaier#ifdef _KERNEL
46126258Smlaier# define DPFPRINTF(format, x...)		\
47126258Smlaier	if (pf_status.debug >= PF_DEBUG_NOISY)	\
48126258Smlaier		printf(format , ##x)
49127145Smlaier#ifdef __FreeBSD__
50126261Smlaiertypedef uma_zone_t pool_t;
51126261Smlaier#else
52126258Smlaiertypedef struct pool pool_t;
53126261Smlaier#endif
54126258Smlaier
55126258Smlaier#else
56126258Smlaier/* Userland equivalents so we can lend code to tcpdump et al. */
57126258Smlaier
58126258Smlaier# include <arpa/inet.h>
59126258Smlaier# include <errno.h>
60126258Smlaier# include <stdio.h>
61126258Smlaier# include <stdlib.h>
62130613Smlaier# include <string.h>
63171168Smlaier# include <netdb.h>
64126258Smlaier# define pool_t			int
65126258Smlaier# define pool_get(pool, flags)	malloc(*(pool))
66126258Smlaier# define pool_put(pool, item)	free(item)
67126258Smlaier# define pool_init(pool, size, a, ao, f, m, p)	(*(pool)) = (size)
68126258Smlaier
69127145Smlaier# ifdef __FreeBSD__
70126261Smlaier# define NTOHS(x) (x) = ntohs((u_int16_t)(x))
71126261Smlaier# endif
72126261Smlaier
73126258Smlaier# ifdef PFDEBUG
74130613Smlaier#  include <sys/stdarg.h>
75126258Smlaier#  define DPFPRINTF(format, x...)	fprintf(stderr, format , ##x)
76126258Smlaier# else
77126258Smlaier#  define DPFPRINTF(format, x...)	((void)0)
78126258Smlaier# endif /* PFDEBUG */
79126258Smlaier#endif /* _KERNEL */
80126258Smlaier
81126258Smlaier
82126258SmlaierSLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
83126258Smlaierpool_t pf_osfp_entry_pl;
84126258Smlaierpool_t pf_osfp_pl;
85126258Smlaier
86126258Smlaierstruct pf_os_fingerprint	*pf_osfp_find(struct pf_osfp_list *,
87126258Smlaier				    struct pf_os_fingerprint *, u_int8_t);
88126258Smlaierstruct pf_os_fingerprint	*pf_osfp_find_exact(struct pf_osfp_list *,
89126258Smlaier				    struct pf_os_fingerprint *);
90126258Smlaiervoid				 pf_osfp_insert(struct pf_osfp_list *,
91126258Smlaier				    struct pf_os_fingerprint *);
92126258Smlaier
93126258Smlaier
94126258Smlaier#ifdef _KERNEL
95126258Smlaier/*
96126258Smlaier * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
97126258Smlaier * Returns the list of possible OSes.
98126258Smlaier */
99126258Smlaierstruct pf_osfp_enlist *
100126258Smlaierpf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off,
101126258Smlaier    const struct tcphdr *tcp)
102126258Smlaier{
103126258Smlaier	struct ip *ip;
104171168Smlaier	struct ip6_hdr *ip6;
105126258Smlaier	char hdr[60];
106126258Smlaier
107171168Smlaier	if ((pd->af != PF_INET && pd->af != PF_INET6) ||
108171168Smlaier	    pd->proto != IPPROTO_TCP || (tcp->th_off << 2) < sizeof(*tcp))
109126258Smlaier		return (NULL);
110126258Smlaier
111171168Smlaier	if (pd->af == PF_INET) {
112171168Smlaier		ip = mtod(m, struct ip *);
113171168Smlaier		ip6 = (struct ip6_hdr *)NULL;
114171168Smlaier	} else {
115171168Smlaier		ip = (struct ip *)NULL;
116171168Smlaier		ip6 = mtod(m, struct ip6_hdr *);
117171168Smlaier	}
118171168Smlaier	if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL,
119171168Smlaier	    pd->af)) return (NULL);
120126258Smlaier
121171168Smlaier	return (pf_osfp_fingerprint_hdr(ip, ip6, (struct tcphdr *)hdr));
122126258Smlaier}
123126258Smlaier#endif /* _KERNEL */
124126258Smlaier
125126258Smlaierstruct pf_osfp_enlist *
126171168Smlaierpf_osfp_fingerprint_hdr(const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcp)
127126258Smlaier{
128126258Smlaier	struct pf_os_fingerprint fp, *fpresult;
129126258Smlaier	int cnt, optlen = 0;
130126261Smlaier	const u_int8_t *optp;
131171168Smlaier#ifdef _KERNEL
132171168Smlaier	char srcname[128];
133171168Smlaier#else
134171168Smlaier	char srcname[NI_MAXHOST];
135171168Smlaier#endif
136126258Smlaier
137171168Smlaier	if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN)
138126258Smlaier		return (NULL);
139171168Smlaier	if (ip) {
140171168Smlaier		if ((ip->ip_off & htons(IP_OFFMASK)) != 0)
141171168Smlaier			return (NULL);
142171168Smlaier	}
143126258Smlaier
144126258Smlaier	memset(&fp, 0, sizeof(fp));
145126258Smlaier
146171168Smlaier	if (ip) {
147171168Smlaier#ifndef _KERNEL
148171168Smlaier		struct sockaddr_in sin;
149171168Smlaier#endif
150171168Smlaier
151171168Smlaier		fp.fp_psize = ntohs(ip->ip_len);
152171168Smlaier		fp.fp_ttl = ip->ip_ttl;
153171168Smlaier		if (ip->ip_off & htons(IP_DF))
154171168Smlaier			fp.fp_flags |= PF_OSFP_DF;
155171168Smlaier#ifdef _KERNEL
156171168Smlaier		strlcpy(srcname, inet_ntoa(ip->ip_src), sizeof(srcname));
157171168Smlaier#else
158171168Smlaier		memset(&sin, 0, sizeof(sin));
159171168Smlaier		sin.sin_family = AF_INET;
160171168Smlaier		sin.sin_len = sizeof(struct sockaddr_in);
161171168Smlaier		sin.sin_addr = ip->ip_src;
162171168Smlaier		(void)getnameinfo((struct sockaddr *)&sin,
163171168Smlaier		    sizeof(struct sockaddr_in), srcname, sizeof(srcname),
164171168Smlaier		    NULL, 0, NI_NUMERICHOST);
165171168Smlaier#endif
166171168Smlaier	}
167171168Smlaier#ifdef INET6
168171168Smlaier	else if (ip6) {
169171168Smlaier#ifndef _KERNEL
170171168Smlaier		struct sockaddr_in6 sin6;
171171168Smlaier#endif
172171168Smlaier
173171168Smlaier		/* jumbo payload? */
174171168Smlaier		fp.fp_psize = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
175171168Smlaier		fp.fp_ttl = ip6->ip6_hlim;
176126258Smlaier		fp.fp_flags |= PF_OSFP_DF;
177171168Smlaier		fp.fp_flags |= PF_OSFP_INET6;
178171168Smlaier#ifdef _KERNEL
179171168Smlaier		strlcpy(srcname, ip6_sprintf((struct in6_addr *)&ip6->ip6_src),
180171168Smlaier		    sizeof(srcname));
181171168Smlaier#else
182171168Smlaier		memset(&sin6, 0, sizeof(sin6));
183171168Smlaier		sin6.sin6_family = AF_INET6;
184171168Smlaier		sin6.sin6_len = sizeof(struct sockaddr_in6);
185171168Smlaier		sin6.sin6_addr = ip6->ip6_src;
186171168Smlaier		(void)getnameinfo((struct sockaddr *)&sin6,
187171168Smlaier		    sizeof(struct sockaddr_in6), srcname, sizeof(srcname),
188171168Smlaier		    NULL, 0, NI_NUMERICHOST);
189171168Smlaier#endif
190171168Smlaier	}
191171168Smlaier#endif
192171168Smlaier	else
193171168Smlaier		return (NULL);
194126258Smlaier	fp.fp_wsize = ntohs(tcp->th_win);
195126258Smlaier
196126258Smlaier
197126258Smlaier	cnt = (tcp->th_off << 2) - sizeof(*tcp);
198126261Smlaier	optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
199126258Smlaier	for (; cnt > 0; cnt -= optlen, optp += optlen) {
200126258Smlaier		if (*optp == TCPOPT_EOL)
201126258Smlaier			break;
202126258Smlaier
203126258Smlaier		fp.fp_optcnt++;
204126258Smlaier		if (*optp == TCPOPT_NOP) {
205126258Smlaier			fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
206126258Smlaier			    PF_OSFP_TCPOPT_NOP;
207126258Smlaier			optlen = 1;
208126258Smlaier		} else {
209126258Smlaier			if (cnt < 2)
210126258Smlaier				return (NULL);
211126258Smlaier			optlen = optp[1];
212126258Smlaier			if (optlen > cnt || optlen < 2)
213126258Smlaier				return (NULL);
214126258Smlaier			switch (*optp) {
215126258Smlaier			case TCPOPT_MAXSEG:
216126258Smlaier				if (optlen >= TCPOLEN_MAXSEG)
217126258Smlaier					memcpy(&fp.fp_mss, &optp[2],
218126258Smlaier					    sizeof(fp.fp_mss));
219126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
220126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
221126258Smlaier				NTOHS(fp.fp_mss);
222126258Smlaier				break;
223126258Smlaier			case TCPOPT_WINDOW:
224126258Smlaier				if (optlen >= TCPOLEN_WINDOW)
225126258Smlaier					memcpy(&fp.fp_wscale, &optp[2],
226126258Smlaier					    sizeof(fp.fp_wscale));
227126258Smlaier				NTOHS(fp.fp_wscale);
228126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
229126258Smlaier				    PF_OSFP_TCPOPT_BITS) |
230126258Smlaier				    PF_OSFP_TCPOPT_WSCALE;
231126258Smlaier				break;
232126258Smlaier			case TCPOPT_SACK_PERMITTED:
233126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
234126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
235126258Smlaier				break;
236126258Smlaier			case TCPOPT_TIMESTAMP:
237126258Smlaier				if (optlen >= TCPOLEN_TIMESTAMP) {
238126258Smlaier					u_int32_t ts;
239126258Smlaier					memcpy(&ts, &optp[2], sizeof(ts));
240126258Smlaier					if (ts == 0)
241126258Smlaier						fp.fp_flags |= PF_OSFP_TS0;
242126258Smlaier
243126258Smlaier				}
244126258Smlaier				fp.fp_tcpopts = (fp.fp_tcpopts <<
245126258Smlaier				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
246126258Smlaier				break;
247126258Smlaier			default:
248126258Smlaier				return (NULL);
249126258Smlaier			}
250126258Smlaier		}
251126258Smlaier		optlen = MAX(optlen, 1);	/* paranoia */
252126258Smlaier	}
253126258Smlaier
254126258Smlaier	DPFPRINTF("fingerprinted %s:%d  %d:%d:%d:%d:%llx (%d) "
255126258Smlaier	    "(TS=%s,M=%s%d,W=%s%d)\n",
256171168Smlaier	    srcname, ntohs(tcp->th_sport),
257126258Smlaier	    fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
258126258Smlaier	    fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
259126258Smlaier	    (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
260126258Smlaier	    (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
261126258Smlaier	    (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
262126258Smlaier	    fp.fp_mss,
263126258Smlaier	    (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
264126258Smlaier	    (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
265126258Smlaier	    fp.fp_wscale);
266126258Smlaier
267126258Smlaier	if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
268126258Smlaier	    PF_OSFP_MAXTTL_OFFSET)))
269126258Smlaier		return (&fpresult->fp_oses);
270126258Smlaier	return (NULL);
271126258Smlaier}
272126258Smlaier
273126258Smlaier/* Match a fingerprint ID against a list of OSes */
274126258Smlaierint
275126258Smlaierpf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
276126258Smlaier{
277126258Smlaier	struct pf_osfp_entry *entry;
278126258Smlaier	int os_class, os_version, os_subtype;
279126258Smlaier	int en_class, en_version, en_subtype;
280126258Smlaier
281126258Smlaier	if (os == PF_OSFP_ANY)
282126258Smlaier		return (1);
283126258Smlaier	if (list == NULL) {
284126258Smlaier		DPFPRINTF("osfp no match against %x\n", os);
285126258Smlaier		return (os == PF_OSFP_UNKNOWN);
286126258Smlaier	}
287126258Smlaier	PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
288126258Smlaier	SLIST_FOREACH(entry, list, fp_entry) {
289126258Smlaier		PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
290126258Smlaier		if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
291126258Smlaier		    (os_version == PF_OSFP_ANY || en_version == os_version) &&
292126258Smlaier		    (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
293126258Smlaier			DPFPRINTF("osfp matched %s %s %s  %x==%x\n",
294126258Smlaier			    entry->fp_class_nm, entry->fp_version_nm,
295126258Smlaier			    entry->fp_subtype_nm, os, entry->fp_os);
296126258Smlaier			return (1);
297126258Smlaier		}
298126258Smlaier	}
299126258Smlaier	DPFPRINTF("fingerprint 0x%x didn't match\n", os);
300126258Smlaier	return (0);
301126258Smlaier}
302126258Smlaier
303126258Smlaier/* Initialize the OS fingerprint system */
304127145Smlaier#ifdef __FreeBSD__
305126261Smlaierint
306126261Smlaier#else
307126258Smlaiervoid
308126261Smlaier#endif
309126258Smlaierpf_osfp_initialize(void)
310126258Smlaier{
311126261Smlaier#if defined(__FreeBSD__) && defined(_KERNEL)
312126261Smlaier	int error = ENOMEM;
313126261Smlaier
314126261Smlaier	do {
315126261Smlaier		pf_osfp_entry_pl = pf_osfp_pl = NULL;
316126261Smlaier		UMA_CREATE(pf_osfp_entry_pl, struct pf_osfp_entry, "pfospfen");
317126261Smlaier		UMA_CREATE(pf_osfp_pl, struct pf_os_fingerprint, "pfosfp");
318126261Smlaier		error = 0;
319126261Smlaier	} while(0);
320126261Smlaier#else
321126258Smlaier	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
322145836Smlaier	    "pfosfpen", &pool_allocator_nointr);
323126258Smlaier	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
324145836Smlaier	    "pfosfp", &pool_allocator_nointr);
325126261Smlaier#endif
326126258Smlaier	SLIST_INIT(&pf_osfp_list);
327127145Smlaier#ifdef __FreeBSD__
328127145Smlaier#ifdef _KERNEL
329126261Smlaier	return (error);
330126261Smlaier#else
331126261Smlaier	return (0);
332126261Smlaier#endif
333126261Smlaier#endif
334126258Smlaier}
335126258Smlaier
336126261Smlaier#if defined(__FreeBSD__) && (_KERNEL)
337126261Smlaiervoid
338126261Smlaierpf_osfp_cleanup(void)
339126261Smlaier{
340126261Smlaier	UMA_DESTROY(pf_osfp_entry_pl);
341126261Smlaier	UMA_DESTROY(pf_osfp_pl);
342126261Smlaier}
343126261Smlaier#endif
344126261Smlaier
345126258Smlaier/* Flush the fingerprint list */
346126258Smlaiervoid
347126258Smlaierpf_osfp_flush(void)
348126258Smlaier{
349126258Smlaier	struct pf_os_fingerprint *fp;
350126258Smlaier	struct pf_osfp_entry *entry;
351126258Smlaier
352126258Smlaier	while ((fp = SLIST_FIRST(&pf_osfp_list))) {
353126258Smlaier		SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
354126258Smlaier		while ((entry = SLIST_FIRST(&fp->fp_oses))) {
355126258Smlaier			SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
356126258Smlaier			pool_put(&pf_osfp_entry_pl, entry);
357126258Smlaier		}
358126258Smlaier		pool_put(&pf_osfp_pl, fp);
359126258Smlaier	}
360126258Smlaier}
361126258Smlaier
362126258Smlaier
363126258Smlaier/* Add a fingerprint */
364126258Smlaierint
365126258Smlaierpf_osfp_add(struct pf_osfp_ioctl *fpioc)
366126258Smlaier{
367126258Smlaier	struct pf_os_fingerprint *fp, fpadd;
368126258Smlaier	struct pf_osfp_entry *entry;
369126258Smlaier
370126258Smlaier	memset(&fpadd, 0, sizeof(fpadd));
371126258Smlaier	fpadd.fp_tcpopts = fpioc->fp_tcpopts;
372126258Smlaier	fpadd.fp_wsize = fpioc->fp_wsize;
373126258Smlaier	fpadd.fp_psize = fpioc->fp_psize;
374126258Smlaier	fpadd.fp_mss = fpioc->fp_mss;
375126258Smlaier	fpadd.fp_flags = fpioc->fp_flags;
376126258Smlaier	fpadd.fp_optcnt = fpioc->fp_optcnt;
377126258Smlaier	fpadd.fp_wscale = fpioc->fp_wscale;
378126258Smlaier	fpadd.fp_ttl = fpioc->fp_ttl;
379126258Smlaier
380126258Smlaier	DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
381126258Smlaier	    "(TS=%s,M=%s%d,W=%s%d) %x\n",
382126258Smlaier	    fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
383126258Smlaier	    fpioc->fp_os.fp_subtype_nm,
384126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
385126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
386126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
387126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
388126258Smlaier	    fpadd.fp_wsize,
389126258Smlaier	    fpadd.fp_ttl,
390126258Smlaier	    (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
391126258Smlaier	    (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
392126258Smlaier	    (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
393126258Smlaier	    fpadd.fp_psize,
394126258Smlaier	    (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
395126258Smlaier	    (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
396126258Smlaier	    (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
397126258Smlaier	    (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
398126258Smlaier	    fpadd.fp_mss,
399126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
400126258Smlaier	    (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
401126258Smlaier	    fpadd.fp_wscale,
402126258Smlaier	    fpioc->fp_os.fp_os);
403126258Smlaier
404126258Smlaier
405126258Smlaier	if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
406126258Smlaier		 SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
407126258Smlaier			if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
408126258Smlaier				return (EEXIST);
409126258Smlaier		}
410126258Smlaier		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL)
411126258Smlaier			return (ENOMEM);
412126258Smlaier	} else {
413126258Smlaier		if ((fp = pool_get(&pf_osfp_pl, PR_NOWAIT)) == NULL)
414126258Smlaier			return (ENOMEM);
415126258Smlaier		memset(fp, 0, sizeof(*fp));
416126258Smlaier		fp->fp_tcpopts = fpioc->fp_tcpopts;
417126258Smlaier		fp->fp_wsize = fpioc->fp_wsize;
418126258Smlaier		fp->fp_psize = fpioc->fp_psize;
419126258Smlaier		fp->fp_mss = fpioc->fp_mss;
420126258Smlaier		fp->fp_flags = fpioc->fp_flags;
421126258Smlaier		fp->fp_optcnt = fpioc->fp_optcnt;
422126258Smlaier		fp->fp_wscale = fpioc->fp_wscale;
423126258Smlaier		fp->fp_ttl = fpioc->fp_ttl;
424126258Smlaier		SLIST_INIT(&fp->fp_oses);
425126258Smlaier		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL) {
426126258Smlaier			pool_put(&pf_osfp_pl, fp);
427126258Smlaier			return (ENOMEM);
428126258Smlaier		}
429126258Smlaier		pf_osfp_insert(&pf_osfp_list, fp);
430126258Smlaier	}
431126258Smlaier	memcpy(entry, &fpioc->fp_os, sizeof(*entry));
432126258Smlaier
433126258Smlaier	/* Make sure the strings are NUL terminated */
434126258Smlaier	entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
435126258Smlaier	entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
436126258Smlaier	entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
437126258Smlaier
438126258Smlaier	SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
439126258Smlaier
440126258Smlaier#ifdef PFDEBUG
441126258Smlaier	if ((fp = pf_osfp_validate()))
442126258Smlaier		printf("Invalid fingerprint list\n");
443126258Smlaier#endif /* PFDEBUG */
444126258Smlaier	return (0);
445126258Smlaier}
446126258Smlaier
447126258Smlaier
448126258Smlaier/* Find a fingerprint in the list */
449126258Smlaierstruct pf_os_fingerprint *
450126258Smlaierpf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
451126258Smlaier    u_int8_t ttldiff)
452126258Smlaier{
453126258Smlaier	struct pf_os_fingerprint *f;
454126258Smlaier
455126258Smlaier#define MATCH_INT(_MOD, _DC, _field)					\
456126258Smlaier	if ((f->fp_flags & _DC) == 0) {					\
457126258Smlaier		if ((f->fp_flags & _MOD) == 0) {			\
458126258Smlaier			if (f->_field != find->_field)			\
459126258Smlaier				continue;				\
460126258Smlaier		} else {						\
461126258Smlaier			if (f->_field == 0 || find->_field % f->_field)	\
462126258Smlaier				continue;				\
463126258Smlaier		}							\
464126258Smlaier	}
465126258Smlaier
466126258Smlaier	SLIST_FOREACH(f, list, fp_next) {
467126258Smlaier		if (f->fp_tcpopts != find->fp_tcpopts ||
468126258Smlaier		    f->fp_optcnt != find->fp_optcnt ||
469126258Smlaier		    f->fp_ttl < find->fp_ttl ||
470126258Smlaier		    f->fp_ttl - find->fp_ttl > ttldiff ||
471126258Smlaier		    (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
472126258Smlaier		    (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
473126258Smlaier			continue;
474126258Smlaier
475126258Smlaier		MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
476126258Smlaier		MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
477126258Smlaier		MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
478126258Smlaier		if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
479126258Smlaier			if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
480126258Smlaier				if (find->fp_mss == 0)
481126258Smlaier					continue;
482126258Smlaier
483126258Smlaier/* Some "smart" NAT devices and DSL routers will tweak the MSS size and
484126258Smlaier * will set it to whatever is suitable for the link type.
485126258Smlaier */
486126258Smlaier#define SMART_MSS	1460
487126258Smlaier				if ((find->fp_wsize % find->fp_mss ||
488126258Smlaier				    find->fp_wsize / find->fp_mss !=
489126258Smlaier				    f->fp_wsize) &&
490126258Smlaier				    (find->fp_wsize % SMART_MSS ||
491126258Smlaier				    find->fp_wsize / SMART_MSS !=
492126258Smlaier				    f->fp_wsize))
493126258Smlaier					continue;
494126258Smlaier			} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
495126258Smlaier				if (find->fp_mss == 0)
496126258Smlaier					continue;
497126258Smlaier
498126258Smlaier#define MTUOFF	(sizeof(struct ip) + sizeof(struct tcphdr))
499126258Smlaier#define SMART_MTU	(SMART_MSS + MTUOFF)
500126258Smlaier				if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
501126258Smlaier				    find->fp_wsize / (find->fp_mss + MTUOFF) !=
502126258Smlaier				    f->fp_wsize) &&
503126258Smlaier				    (find->fp_wsize % SMART_MTU ||
504126258Smlaier				    find->fp_wsize / SMART_MTU !=
505126258Smlaier				    f->fp_wsize))
506126258Smlaier					continue;
507126258Smlaier			} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
508126258Smlaier				if (f->fp_wsize == 0 || find->fp_wsize %
509126258Smlaier				    f->fp_wsize)
510126258Smlaier					continue;
511126258Smlaier			} else {
512126258Smlaier				if (f->fp_wsize != find->fp_wsize)
513126258Smlaier					continue;
514126258Smlaier			}
515126258Smlaier		}
516126258Smlaier		return (f);
517126258Smlaier	}
518126258Smlaier
519126258Smlaier	return (NULL);
520126258Smlaier}
521126258Smlaier
522126258Smlaier/* Find an exact fingerprint in the list */
523126258Smlaierstruct pf_os_fingerprint *
524126258Smlaierpf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
525126258Smlaier{
526126258Smlaier	struct pf_os_fingerprint *f;
527126258Smlaier
528126258Smlaier	SLIST_FOREACH(f, list, fp_next) {
529126258Smlaier		if (f->fp_tcpopts == find->fp_tcpopts &&
530126258Smlaier		    f->fp_wsize == find->fp_wsize &&
531126258Smlaier		    f->fp_psize == find->fp_psize &&
532126258Smlaier		    f->fp_mss == find->fp_mss &&
533126258Smlaier		    f->fp_flags == find->fp_flags &&
534126258Smlaier		    f->fp_optcnt == find->fp_optcnt &&
535126258Smlaier		    f->fp_wscale == find->fp_wscale &&
536126258Smlaier		    f->fp_ttl == find->fp_ttl)
537126258Smlaier			return (f);
538126258Smlaier	}
539126258Smlaier
540126258Smlaier	return (NULL);
541126258Smlaier}
542126258Smlaier
543126258Smlaier/* Insert a fingerprint into the list */
544126258Smlaiervoid
545126258Smlaierpf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
546126258Smlaier{
547126258Smlaier	struct pf_os_fingerprint *f, *prev = NULL;
548126258Smlaier
549126258Smlaier	/* XXX need to go semi tree based.  can key on tcp options */
550126258Smlaier
551126258Smlaier	SLIST_FOREACH(f, list, fp_next)
552126258Smlaier		prev = f;
553126258Smlaier	if (prev)
554126258Smlaier		SLIST_INSERT_AFTER(prev, ins, fp_next);
555126258Smlaier	else
556126258Smlaier		SLIST_INSERT_HEAD(list, ins, fp_next);
557126258Smlaier}
558126258Smlaier
559126258Smlaier/* Fill a fingerprint by its number (from an ioctl) */
560126258Smlaierint
561126258Smlaierpf_osfp_get(struct pf_osfp_ioctl *fpioc)
562126258Smlaier{
563126258Smlaier	struct pf_os_fingerprint *fp;
564126258Smlaier	struct pf_osfp_entry *entry;
565126258Smlaier	int num = fpioc->fp_getnum;
566126258Smlaier	int i = 0;
567126258Smlaier
568126258Smlaier
569126258Smlaier	memset(fpioc, 0, sizeof(*fpioc));
570126258Smlaier	SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
571126258Smlaier		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
572126258Smlaier			if (i++ == num) {
573126258Smlaier				fpioc->fp_mss = fp->fp_mss;
574126258Smlaier				fpioc->fp_wsize = fp->fp_wsize;
575126258Smlaier				fpioc->fp_flags = fp->fp_flags;
576126258Smlaier				fpioc->fp_psize = fp->fp_psize;
577126258Smlaier				fpioc->fp_ttl = fp->fp_ttl;
578126258Smlaier				fpioc->fp_wscale = fp->fp_wscale;
579126258Smlaier				fpioc->fp_getnum = num;
580126258Smlaier				memcpy(&fpioc->fp_os, entry,
581126258Smlaier				    sizeof(fpioc->fp_os));
582126258Smlaier				return (0);
583126258Smlaier			}
584126258Smlaier		}
585126258Smlaier	}
586126258Smlaier
587126258Smlaier	return (EBUSY);
588126258Smlaier}
589126258Smlaier
590126258Smlaier
591126258Smlaier/* Validate that each signature is reachable */
592126258Smlaierstruct pf_os_fingerprint *
593126258Smlaierpf_osfp_validate(void)
594126258Smlaier{
595126258Smlaier	struct pf_os_fingerprint *f, *f2, find;
596126258Smlaier
597126258Smlaier	SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
598126258Smlaier		memcpy(&find, f, sizeof(find));
599126258Smlaier
600126258Smlaier		/* We do a few MSS/th_win percolations to make things unique */
601126258Smlaier		if (find.fp_mss == 0)
602126258Smlaier			find.fp_mss = 128;
603126258Smlaier		if (f->fp_flags & PF_OSFP_WSIZE_MSS)
604217388Scsjp			find.fp_wsize *= find.fp_mss;
605126258Smlaier		else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
606126258Smlaier			find.fp_wsize *= (find.fp_mss + 40);
607126258Smlaier		else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
608126258Smlaier			find.fp_wsize *= 2;
609126258Smlaier		if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
610126258Smlaier			if (f2)
611126258Smlaier				printf("Found \"%s %s %s\" instead of "
612126258Smlaier				    "\"%s %s %s\"\n",
613126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
614126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
615126258Smlaier				    SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
616126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
617126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
618126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
619126258Smlaier			else
620126258Smlaier				printf("Couldn't find \"%s %s %s\"\n",
621126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
622126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
623126258Smlaier				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
624126258Smlaier			return (f);
625126258Smlaier		}
626126258Smlaier	}
627126258Smlaier	return (NULL);
628126258Smlaier}
629