Home | History | Annotate | Line # | Download | only in net
      1  1.13  christos /*	$NetBSD: pf_osfp.c,v 1.13 2017/01/16 15:46:19 christos 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.13  christos __KERNEL_RCSID(0, "$NetBSD: pf_osfp.c,v 1.13 2017/01/16 15:46:19 christos 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.13  christos 		in6_print(srcname, sizeof(srcname),
    176  1.13  christos 		    (const struct in6_addr *)&ip6->ip6_src);
    177   1.8      yamt #else
    178   1.8      yamt 		memset(&sin6, 0, sizeof(sin6));
    179   1.8      yamt 		sin6.sin6_family = AF_INET6;
    180   1.8      yamt 		sin6.sin6_len = sizeof(struct sockaddr_in6);
    181   1.8      yamt 		sin6.sin6_addr = ip6->ip6_src;
    182   1.8      yamt 		(void)getnameinfo((struct sockaddr *)&sin6,
    183   1.8      yamt 		    sizeof(struct sockaddr_in6), srcname, sizeof(srcname),
    184   1.8      yamt 		    NULL, 0, NI_NUMERICHOST);
    185   1.8      yamt #endif
    186   1.8      yamt 	}
    187   1.8      yamt #endif
    188   1.8      yamt 	else
    189   1.8      yamt 		return (NULL);
    190   1.1    itojun 	fp.fp_wsize = ntohs(tcp->th_win);
    191   1.1    itojun 
    192   1.1    itojun 
    193   1.1    itojun 	cnt = (tcp->th_off << 2) - sizeof(*tcp);
    194   1.1    itojun 	optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
    195   1.1    itojun 	for (; cnt > 0; cnt -= optlen, optp += optlen) {
    196   1.1    itojun 		if (*optp == TCPOPT_EOL)
    197   1.1    itojun 			break;
    198   1.1    itojun 
    199   1.1    itojun 		fp.fp_optcnt++;
    200   1.1    itojun 		if (*optp == TCPOPT_NOP) {
    201   1.1    itojun 			fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
    202   1.1    itojun 			    PF_OSFP_TCPOPT_NOP;
    203   1.1    itojun 			optlen = 1;
    204   1.1    itojun 		} else {
    205   1.1    itojun 			if (cnt < 2)
    206   1.1    itojun 				return (NULL);
    207   1.1    itojun 			optlen = optp[1];
    208   1.1    itojun 			if (optlen > cnt || optlen < 2)
    209   1.1    itojun 				return (NULL);
    210   1.1    itojun 			switch (*optp) {
    211   1.1    itojun 			case TCPOPT_MAXSEG:
    212   1.1    itojun 				if (optlen >= TCPOLEN_MAXSEG)
    213   1.1    itojun 					memcpy(&fp.fp_mss, &optp[2],
    214   1.1    itojun 					    sizeof(fp.fp_mss));
    215   1.1    itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    216   1.1    itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
    217   1.1    itojun 				NTOHS(fp.fp_mss);
    218   1.1    itojun 				break;
    219   1.1    itojun 			case TCPOPT_WINDOW:
    220   1.1    itojun 				if (optlen >= TCPOLEN_WINDOW)
    221   1.1    itojun 					memcpy(&fp.fp_wscale, &optp[2],
    222   1.1    itojun 					    sizeof(fp.fp_wscale));
    223   1.1    itojun 				NTOHS(fp.fp_wscale);
    224   1.1    itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    225   1.1    itojun 				    PF_OSFP_TCPOPT_BITS) |
    226   1.1    itojun 				    PF_OSFP_TCPOPT_WSCALE;
    227   1.1    itojun 				break;
    228   1.1    itojun 			case TCPOPT_SACK_PERMITTED:
    229   1.1    itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    230   1.1    itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
    231   1.1    itojun 				break;
    232   1.1    itojun 			case TCPOPT_TIMESTAMP:
    233   1.1    itojun 				if (optlen >= TCPOLEN_TIMESTAMP) {
    234   1.1    itojun 					u_int32_t ts;
    235   1.1    itojun 					memcpy(&ts, &optp[2], sizeof(ts));
    236   1.1    itojun 					if (ts == 0)
    237   1.1    itojun 						fp.fp_flags |= PF_OSFP_TS0;
    238   1.1    itojun 
    239   1.1    itojun 				}
    240   1.1    itojun 				fp.fp_tcpopts = (fp.fp_tcpopts <<
    241   1.1    itojun 				    PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
    242   1.1    itojun 				break;
    243   1.1    itojun 			default:
    244   1.1    itojun 				return (NULL);
    245   1.1    itojun 			}
    246   1.1    itojun 		}
    247   1.1    itojun 		optlen = MAX(optlen, 1);	/* paranoia */
    248   1.1    itojun 	}
    249   1.1    itojun 
    250   1.1    itojun 	DPFPRINTF("fingerprinted %s:%d  %d:%d:%d:%d:%llx (%d) "
    251   1.1    itojun 	    "(TS=%s,M=%s%d,W=%s%d)\n",
    252   1.8      yamt 	    srcname, ntohs(tcp->th_sport),
    253   1.1    itojun 	    fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
    254   1.1    itojun 	    fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
    255   1.1    itojun 	    (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
    256   1.1    itojun 	    (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
    257   1.1    itojun 	    (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
    258   1.1    itojun 	    fp.fp_mss,
    259   1.1    itojun 	    (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
    260   1.1    itojun 	    (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
    261   1.1    itojun 	    fp.fp_wscale);
    262   1.1    itojun 
    263   1.1    itojun 	if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
    264   1.1    itojun 	    PF_OSFP_MAXTTL_OFFSET)))
    265   1.1    itojun 		return (&fpresult->fp_oses);
    266   1.1    itojun 	return (NULL);
    267   1.1    itojun }
    268   1.1    itojun 
    269   1.1    itojun /* Match a fingerprint ID against a list of OSes */
    270   1.1    itojun int
    271   1.1    itojun pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
    272   1.1    itojun {
    273   1.1    itojun 	struct pf_osfp_entry *entry;
    274   1.1    itojun 	int os_class, os_version, os_subtype;
    275   1.1    itojun 	int en_class, en_version, en_subtype;
    276   1.1    itojun 
    277   1.1    itojun 	if (os == PF_OSFP_ANY)
    278   1.1    itojun 		return (1);
    279   1.1    itojun 	if (list == NULL) {
    280   1.1    itojun 		DPFPRINTF("osfp no match against %x\n", os);
    281   1.1    itojun 		return (os == PF_OSFP_UNKNOWN);
    282   1.1    itojun 	}
    283   1.1    itojun 	PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
    284   1.1    itojun 	SLIST_FOREACH(entry, list, fp_entry) {
    285   1.1    itojun 		PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
    286   1.1    itojun 		if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
    287   1.1    itojun 		    (os_version == PF_OSFP_ANY || en_version == os_version) &&
    288   1.1    itojun 		    (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
    289   1.1    itojun 			DPFPRINTF("osfp matched %s %s %s  %x==%x\n",
    290   1.1    itojun 			    entry->fp_class_nm, entry->fp_version_nm,
    291   1.1    itojun 			    entry->fp_subtype_nm, os, entry->fp_os);
    292   1.1    itojun 			return (1);
    293   1.1    itojun 		}
    294   1.1    itojun 	}
    295   1.1    itojun 	DPFPRINTF("fingerprint 0x%x didn't match\n", os);
    296   1.1    itojun 	return (0);
    297   1.1    itojun }
    298   1.1    itojun 
    299   1.1    itojun /* Initialize the OS fingerprint system */
    300   1.1    itojun void
    301   1.1    itojun pf_osfp_initialize(void)
    302   1.1    itojun {
    303   1.6        ad #ifdef __NetBSD__
    304   1.6        ad 	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
    305   1.6        ad 	    "pfosfpen", &pool_allocator_nointr, IPL_NONE);
    306   1.6        ad 	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
    307   1.6        ad 	    "pfosfp", &pool_allocator_nointr, IPL_NONE);
    308   1.6        ad #else
    309   1.1    itojun 	pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
    310   1.4      yamt 	    "pfosfpen", &pool_allocator_nointr);
    311   1.1    itojun 	pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
    312   1.4      yamt 	    "pfosfp", &pool_allocator_nointr);
    313   1.8      yamt #endif /* !__NetBSD__ */
    314   1.1    itojun 	SLIST_INIT(&pf_osfp_list);
    315   1.1    itojun }
    316   1.1    itojun 
    317  1.10     ahoka #ifdef _MODULE
    318  1.10     ahoka void
    319  1.10     ahoka pf_osfp_destroy(void)
    320  1.10     ahoka {
    321  1.10     ahoka 	pf_osfp_flush();
    322  1.10     ahoka 
    323  1.10     ahoka 	pool_destroy(&pf_osfp_pl);
    324  1.10     ahoka 	pool_destroy(&pf_osfp_entry_pl);
    325  1.10     ahoka }
    326  1.10     ahoka #endif /* _MODULE */
    327  1.10     ahoka 
    328   1.1    itojun /* Flush the fingerprint list */
    329   1.1    itojun void
    330   1.1    itojun pf_osfp_flush(void)
    331   1.1    itojun {
    332   1.1    itojun 	struct pf_os_fingerprint *fp;
    333   1.1    itojun 	struct pf_osfp_entry *entry;
    334   1.1    itojun 
    335   1.1    itojun 	while ((fp = SLIST_FIRST(&pf_osfp_list))) {
    336   1.1    itojun 		SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
    337   1.1    itojun 		while ((entry = SLIST_FIRST(&fp->fp_oses))) {
    338   1.1    itojun 			SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
    339   1.1    itojun 			pool_put(&pf_osfp_entry_pl, entry);
    340   1.1    itojun 		}
    341   1.1    itojun 		pool_put(&pf_osfp_pl, fp);
    342   1.1    itojun 	}
    343   1.1    itojun }
    344   1.1    itojun 
    345   1.1    itojun 
    346   1.1    itojun /* Add a fingerprint */
    347   1.1    itojun int
    348   1.1    itojun pf_osfp_add(struct pf_osfp_ioctl *fpioc)
    349   1.1    itojun {
    350   1.1    itojun 	struct pf_os_fingerprint *fp, fpadd;
    351   1.1    itojun 	struct pf_osfp_entry *entry;
    352   1.1    itojun 
    353   1.1    itojun 	memset(&fpadd, 0, sizeof(fpadd));
    354   1.1    itojun 	fpadd.fp_tcpopts = fpioc->fp_tcpopts;
    355   1.1    itojun 	fpadd.fp_wsize = fpioc->fp_wsize;
    356   1.1    itojun 	fpadd.fp_psize = fpioc->fp_psize;
    357   1.1    itojun 	fpadd.fp_mss = fpioc->fp_mss;
    358   1.1    itojun 	fpadd.fp_flags = fpioc->fp_flags;
    359   1.1    itojun 	fpadd.fp_optcnt = fpioc->fp_optcnt;
    360   1.1    itojun 	fpadd.fp_wscale = fpioc->fp_wscale;
    361   1.1    itojun 	fpadd.fp_ttl = fpioc->fp_ttl;
    362   1.1    itojun 
    363   1.1    itojun 	DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
    364   1.1    itojun 	    "(TS=%s,M=%s%d,W=%s%d) %x\n",
    365   1.1    itojun 	    fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
    366   1.1    itojun 	    fpioc->fp_os.fp_subtype_nm,
    367   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
    368   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
    369   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
    370   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
    371   1.1    itojun 	    fpadd.fp_wsize,
    372   1.1    itojun 	    fpadd.fp_ttl,
    373   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
    374   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
    375   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
    376   1.1    itojun 	    fpadd.fp_psize,
    377   1.1    itojun 	    (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
    378   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
    379   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
    380   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
    381   1.1    itojun 	    fpadd.fp_mss,
    382   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
    383   1.1    itojun 	    (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
    384   1.1    itojun 	    fpadd.fp_wscale,
    385   1.1    itojun 	    fpioc->fp_os.fp_os);
    386   1.1    itojun 
    387   1.1    itojun 
    388   1.1    itojun 	if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
    389   1.1    itojun 		 SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
    390   1.1    itojun 			if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
    391   1.1    itojun 				return (EEXIST);
    392   1.1    itojun 		}
    393   1.1    itojun 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL)
    394   1.1    itojun 			return (ENOMEM);
    395   1.1    itojun 	} else {
    396   1.1    itojun 		if ((fp = pool_get(&pf_osfp_pl, PR_NOWAIT)) == NULL)
    397   1.1    itojun 			return (ENOMEM);
    398   1.1    itojun 		memset(fp, 0, sizeof(*fp));
    399   1.1    itojun 		fp->fp_tcpopts = fpioc->fp_tcpopts;
    400   1.1    itojun 		fp->fp_wsize = fpioc->fp_wsize;
    401   1.1    itojun 		fp->fp_psize = fpioc->fp_psize;
    402   1.1    itojun 		fp->fp_mss = fpioc->fp_mss;
    403   1.1    itojun 		fp->fp_flags = fpioc->fp_flags;
    404   1.1    itojun 		fp->fp_optcnt = fpioc->fp_optcnt;
    405   1.1    itojun 		fp->fp_wscale = fpioc->fp_wscale;
    406   1.1    itojun 		fp->fp_ttl = fpioc->fp_ttl;
    407   1.1    itojun 		SLIST_INIT(&fp->fp_oses);
    408   1.1    itojun 		if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL) {
    409   1.1    itojun 			pool_put(&pf_osfp_pl, fp);
    410   1.1    itojun 			return (ENOMEM);
    411   1.1    itojun 		}
    412   1.1    itojun 		pf_osfp_insert(&pf_osfp_list, fp);
    413   1.1    itojun 	}
    414   1.1    itojun 	memcpy(entry, &fpioc->fp_os, sizeof(*entry));
    415   1.1    itojun 
    416   1.1    itojun 	/* Make sure the strings are NUL terminated */
    417   1.1    itojun 	entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
    418   1.1    itojun 	entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
    419   1.1    itojun 	entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
    420   1.1    itojun 
    421   1.1    itojun 	SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
    422   1.1    itojun 
    423   1.1    itojun #ifdef PFDEBUG
    424   1.1    itojun 	if ((fp = pf_osfp_validate()))
    425   1.1    itojun 		printf("Invalid fingerprint list\n");
    426   1.1    itojun #endif /* PFDEBUG */
    427   1.1    itojun 	return (0);
    428   1.1    itojun }
    429   1.1    itojun 
    430   1.1    itojun 
    431   1.1    itojun /* Find a fingerprint in the list */
    432   1.1    itojun struct pf_os_fingerprint *
    433   1.1    itojun pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
    434   1.1    itojun     u_int8_t ttldiff)
    435   1.1    itojun {
    436   1.1    itojun 	struct pf_os_fingerprint *f;
    437   1.1    itojun 
    438   1.1    itojun #define MATCH_INT(_MOD, _DC, _field)					\
    439   1.1    itojun 	if ((f->fp_flags & _DC) == 0) {					\
    440   1.1    itojun 		if ((f->fp_flags & _MOD) == 0) {			\
    441   1.1    itojun 			if (f->_field != find->_field)			\
    442   1.1    itojun 				continue;				\
    443   1.1    itojun 		} else {						\
    444   1.1    itojun 			if (f->_field == 0 || find->_field % f->_field)	\
    445   1.1    itojun 				continue;				\
    446   1.1    itojun 		}							\
    447   1.1    itojun 	}
    448   1.1    itojun 
    449   1.1    itojun 	SLIST_FOREACH(f, list, fp_next) {
    450   1.1    itojun 		if (f->fp_tcpopts != find->fp_tcpopts ||
    451   1.1    itojun 		    f->fp_optcnt != find->fp_optcnt ||
    452   1.1    itojun 		    f->fp_ttl < find->fp_ttl ||
    453   1.1    itojun 		    f->fp_ttl - find->fp_ttl > ttldiff ||
    454   1.1    itojun 		    (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
    455   1.1    itojun 		    (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
    456   1.1    itojun 			continue;
    457   1.1    itojun 
    458   1.1    itojun 		MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
    459   1.1    itojun 		MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
    460   1.1    itojun 		MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
    461   1.1    itojun 		if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
    462   1.1    itojun 			if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
    463   1.1    itojun 				if (find->fp_mss == 0)
    464   1.1    itojun 					continue;
    465   1.1    itojun 
    466   1.1    itojun /* Some "smart" NAT devices and DSL routers will tweak the MSS size and
    467   1.1    itojun  * will set it to whatever is suitable for the link type.
    468   1.1    itojun  */
    469   1.1    itojun #define SMART_MSS	1460
    470   1.1    itojun 				if ((find->fp_wsize % find->fp_mss ||
    471   1.1    itojun 				    find->fp_wsize / find->fp_mss !=
    472   1.1    itojun 				    f->fp_wsize) &&
    473   1.1    itojun 				    (find->fp_wsize % SMART_MSS ||
    474   1.1    itojun 				    find->fp_wsize / SMART_MSS !=
    475   1.1    itojun 				    f->fp_wsize))
    476   1.1    itojun 					continue;
    477   1.1    itojun 			} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
    478   1.1    itojun 				if (find->fp_mss == 0)
    479   1.1    itojun 					continue;
    480   1.1    itojun 
    481   1.1    itojun #define MTUOFF	(sizeof(struct ip) + sizeof(struct tcphdr))
    482   1.1    itojun #define SMART_MTU	(SMART_MSS + MTUOFF)
    483   1.1    itojun 				if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
    484   1.1    itojun 				    find->fp_wsize / (find->fp_mss + MTUOFF) !=
    485   1.1    itojun 				    f->fp_wsize) &&
    486   1.1    itojun 				    (find->fp_wsize % SMART_MTU ||
    487   1.1    itojun 				    find->fp_wsize / SMART_MTU !=
    488   1.1    itojun 				    f->fp_wsize))
    489   1.1    itojun 					continue;
    490   1.1    itojun 			} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
    491   1.1    itojun 				if (f->fp_wsize == 0 || find->fp_wsize %
    492   1.1    itojun 				    f->fp_wsize)
    493   1.1    itojun 					continue;
    494   1.1    itojun 			} else {
    495   1.1    itojun 				if (f->fp_wsize != find->fp_wsize)
    496   1.1    itojun 					continue;
    497   1.1    itojun 			}
    498   1.1    itojun 		}
    499   1.1    itojun 		return (f);
    500   1.1    itojun 	}
    501   1.1    itojun 
    502   1.1    itojun 	return (NULL);
    503   1.1    itojun }
    504   1.1    itojun 
    505   1.1    itojun /* Find an exact fingerprint in the list */
    506   1.1    itojun struct pf_os_fingerprint *
    507   1.1    itojun pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
    508   1.1    itojun {
    509   1.1    itojun 	struct pf_os_fingerprint *f;
    510   1.1    itojun 
    511   1.1    itojun 	SLIST_FOREACH(f, list, fp_next) {
    512   1.1    itojun 		if (f->fp_tcpopts == find->fp_tcpopts &&
    513   1.1    itojun 		    f->fp_wsize == find->fp_wsize &&
    514   1.1    itojun 		    f->fp_psize == find->fp_psize &&
    515   1.1    itojun 		    f->fp_mss == find->fp_mss &&
    516   1.1    itojun 		    f->fp_flags == find->fp_flags &&
    517   1.1    itojun 		    f->fp_optcnt == find->fp_optcnt &&
    518   1.1    itojun 		    f->fp_wscale == find->fp_wscale &&
    519   1.1    itojun 		    f->fp_ttl == find->fp_ttl)
    520   1.1    itojun 			return (f);
    521   1.1    itojun 	}
    522   1.1    itojun 
    523   1.1    itojun 	return (NULL);
    524   1.1    itojun }
    525   1.1    itojun 
    526   1.1    itojun /* Insert a fingerprint into the list */
    527   1.1    itojun void
    528   1.1    itojun pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
    529   1.1    itojun {
    530   1.1    itojun 	struct pf_os_fingerprint *f, *prev = NULL;
    531   1.1    itojun 
    532   1.1    itojun 	/* XXX need to go semi tree based.  can key on tcp options */
    533   1.1    itojun 
    534   1.1    itojun 	SLIST_FOREACH(f, list, fp_next)
    535   1.1    itojun 		prev = f;
    536   1.1    itojun 	if (prev)
    537   1.1    itojun 		SLIST_INSERT_AFTER(prev, ins, fp_next);
    538   1.1    itojun 	else
    539   1.1    itojun 		SLIST_INSERT_HEAD(list, ins, fp_next);
    540   1.1    itojun }
    541   1.1    itojun 
    542   1.1    itojun /* Fill a fingerprint by its number (from an ioctl) */
    543   1.1    itojun int
    544   1.1    itojun pf_osfp_get(struct pf_osfp_ioctl *fpioc)
    545   1.1    itojun {
    546   1.1    itojun 	struct pf_os_fingerprint *fp;
    547   1.1    itojun 	struct pf_osfp_entry *entry;
    548   1.1    itojun 	int num = fpioc->fp_getnum;
    549   1.1    itojun 	int i = 0;
    550   1.1    itojun 
    551   1.1    itojun 
    552   1.1    itojun 	memset(fpioc, 0, sizeof(*fpioc));
    553   1.1    itojun 	SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
    554   1.1    itojun 		SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
    555   1.1    itojun 			if (i++ == num) {
    556   1.1    itojun 				fpioc->fp_mss = fp->fp_mss;
    557   1.1    itojun 				fpioc->fp_wsize = fp->fp_wsize;
    558   1.1    itojun 				fpioc->fp_flags = fp->fp_flags;
    559   1.1    itojun 				fpioc->fp_psize = fp->fp_psize;
    560   1.1    itojun 				fpioc->fp_ttl = fp->fp_ttl;
    561   1.1    itojun 				fpioc->fp_wscale = fp->fp_wscale;
    562   1.1    itojun 				fpioc->fp_getnum = num;
    563   1.1    itojun 				memcpy(&fpioc->fp_os, entry,
    564   1.1    itojun 				    sizeof(fpioc->fp_os));
    565   1.1    itojun 				return (0);
    566   1.1    itojun 			}
    567   1.1    itojun 		}
    568   1.1    itojun 	}
    569   1.1    itojun 
    570   1.1    itojun 	return (EBUSY);
    571   1.1    itojun }
    572   1.1    itojun 
    573   1.1    itojun 
    574   1.1    itojun /* Validate that each signature is reachable */
    575   1.1    itojun struct pf_os_fingerprint *
    576   1.1    itojun pf_osfp_validate(void)
    577   1.1    itojun {
    578   1.1    itojun 	struct pf_os_fingerprint *f, *f2, find;
    579   1.1    itojun 
    580   1.1    itojun 	SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
    581   1.1    itojun 		memcpy(&find, f, sizeof(find));
    582   1.1    itojun 
    583   1.1    itojun 		/* We do a few MSS/th_win percolations to make things unique */
    584   1.1    itojun 		if (find.fp_mss == 0)
    585   1.1    itojun 			find.fp_mss = 128;
    586   1.1    itojun 		if (f->fp_flags & PF_OSFP_WSIZE_MSS)
    587  1.11  drochner 			find.fp_wsize *= find.fp_mss;
    588   1.1    itojun 		else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
    589   1.1    itojun 			find.fp_wsize *= (find.fp_mss + 40);
    590   1.1    itojun 		else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
    591   1.1    itojun 			find.fp_wsize *= 2;
    592   1.1    itojun 		if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
    593   1.1    itojun 			if (f2)
    594   1.1    itojun 				printf("Found \"%s %s %s\" instead of "
    595   1.1    itojun 				    "\"%s %s %s\"\n",
    596   1.1    itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
    597   1.1    itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
    598   1.1    itojun 				    SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
    599   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
    600   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
    601   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
    602   1.1    itojun 			else
    603   1.1    itojun 				printf("Couldn't find \"%s %s %s\"\n",
    604   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_class_nm,
    605   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_version_nm,
    606   1.1    itojun 				    SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
    607   1.1    itojun 			return (f);
    608   1.1    itojun 		}
    609   1.1    itojun 	}
    610   1.1    itojun 	return (NULL);
    611   1.1    itojun }
    612