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