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