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