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