]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/pf/pfctl/pfctl_osfp.c
This commit was generated by cvs2svn to compensate for changes in r170263,
[FreeBSD/FreeBSD.git] / contrib / pf / pfctl / pfctl_osfp.c
1 /*      $OpenBSD: pfctl_osfp.c,v 1.12 2005/02/17 13:18:00 aaron Exp $ */
2
3 /*
4  * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.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 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22
23 #include <net/if.h>
24 #include <net/pfvar.h>
25
26 #include <ctype.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "pfctl_parser.h"
34 #include "pfctl.h"
35
36 #ifndef MIN
37 # define MIN(a,b)       (((a) < (b)) ? (a) : (b))
38 #endif /* MIN */
39 #ifndef MAX
40 # define MAX(a,b)       (((a) > (b)) ? (a) : (b))
41 #endif /* MAX */
42
43
44 #if 0
45 # define DEBUG(fp, str, v...) \
46         fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \
47             (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v);
48 #else
49 # define DEBUG(fp, str, v...) ((void)0)
50 #endif
51
52
53 struct name_entry;
54 LIST_HEAD(name_list, name_entry);
55 struct name_entry {
56         LIST_ENTRY(name_entry)  nm_entry;
57         int                     nm_num;
58         char                    nm_name[PF_OSFP_LEN];
59
60         struct name_list        nm_sublist;
61         int                     nm_sublist_num;
62 };
63 struct name_list classes = LIST_HEAD_INITIALIZER(&classes);
64 int class_count;
65 int fingerprint_count;
66
67 void                     add_fingerprint(int, int, struct pf_osfp_ioctl *);
68 struct name_entry       *fingerprint_name_entry(struct name_list *, char *);
69 void                     pfctl_flush_my_fingerprints(struct name_list *);
70 char                    *get_field(char **, size_t *, int *);
71 int                      get_int(char **, size_t *, int *, int *, const char *,
72                              int, int, const char *, int);
73 int                      get_str(char **, size_t *, char **, const char *, int,
74                              const char *, int);
75 int                      get_tcpopts(const char *, int, const char *,
76                             pf_tcpopts_t *, int *, int *, int *, int *, int *,
77                             int *);
78 void                     import_fingerprint(struct pf_osfp_ioctl *);
79 const char              *print_ioctl(struct pf_osfp_ioctl *);
80 void                     print_name_list(int, struct name_list *, const char *);
81 void                     sort_name_list(int, struct name_list *);
82 struct name_entry       *lookup_name_list(struct name_list *, const char *);
83
84 /* Load fingerprints from a file */
85 int
86 pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
87 {
88         FILE *in;
89         char *line;
90         size_t len;
91         int i, lineno = 0;
92         int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale,
93             wscale_mod, optcnt, ts0;
94         pf_tcpopts_t packed_tcpopts;
95         char *class, *version, *subtype, *desc, *tcpopts;
96         struct pf_osfp_ioctl fp;
97
98         pfctl_flush_my_fingerprints(&classes);
99
100         if ((in = pfctl_fopen(fp_filename, "r")) == NULL) {
101                 warn("%s", fp_filename);
102                 return (1);
103         }
104         class = version = subtype = desc = tcpopts = NULL;
105
106         if ((opts & PF_OPT_NOACTION) == 0)
107                 pfctl_clear_fingerprints(dev, opts);
108
109         while ((line = fgetln(in, &len)) != NULL) {
110                 lineno++;
111                 if (class)
112                         free(class);
113                 if (version)
114                         free(version);
115                 if (subtype)
116                         free(subtype);
117                 if (desc)
118                         free(desc);
119                 if (tcpopts)
120                         free(tcpopts);
121                 class = version = subtype = desc = tcpopts = NULL;
122                 memset(&fp, 0, sizeof(fp));
123
124                 /* Chop off comment */
125                 for (i = 0; i < len; i++)
126                         if (line[i] == '#') {
127                                 len = i;
128                                 break;
129                         }
130                 /* Chop off whitespace */
131                 while (len > 0 && isspace(line[len - 1]))
132                         len--;
133                 while (len > 0 && isspace(line[0])) {
134                         len--;
135                         line++;
136                 }
137                 if (len == 0)
138                         continue;
139
140 #define T_DC    0x01    /* Allow don't care */
141 #define T_MSS   0x02    /* Allow MSS multiple */
142 #define T_MTU   0x04    /* Allow MTU multiple */
143 #define T_MOD   0x08    /* Allow modulus */
144
145 #define GET_INT(v, mod, n, ty, mx) \
146         get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno)
147 #define GET_STR(v, n, mn) \
148         get_str(&line, &len, &v, n, mn, fp_filename, lineno)
149
150                 if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU|
151                     T_MOD, 0xffff) ||
152                     GET_INT(ttl, NULL, "ttl", 0, 0xff) ||
153                     GET_INT(df, NULL, "don't fragment frag", 0, 1) ||
154                     GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC,
155                     8192) ||
156                     GET_STR(tcpopts, "TCP Options", 1) ||
157                     GET_STR(class, "OS class", 1) ||
158                     GET_STR(version, "OS version", 0) ||
159                     GET_STR(subtype, "OS subtype", 0) ||
160                     GET_STR(desc, "OS description", 2))
161                         continue;
162                 if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts,
163                     &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0))
164                         continue;
165                 if (len != 0) {
166                         fprintf(stderr, "%s:%d excess field\n", fp_filename,
167                             lineno);
168                         continue;
169                 }
170
171                 fp.fp_ttl = ttl;
172                 if (df)
173                         fp.fp_flags |= PF_OSFP_DF;
174                 switch (w_mod) {
175                 case 0:
176                         break;
177                 case T_DC:
178                         fp.fp_flags |= PF_OSFP_WSIZE_DC;
179                         break;
180                 case T_MSS:
181                         fp.fp_flags |= PF_OSFP_WSIZE_MSS;
182                         break;
183                 case T_MTU:
184                         fp.fp_flags |= PF_OSFP_WSIZE_MTU;
185                         break;
186                 case T_MOD:
187                         fp.fp_flags |= PF_OSFP_WSIZE_MOD;
188                         break;
189                 }
190                 fp.fp_wsize = window;
191
192                 switch (p_mod) {
193                 case T_DC:
194                         fp.fp_flags |= PF_OSFP_PSIZE_DC;
195                         break;
196                 case T_MOD:
197                         fp.fp_flags |= PF_OSFP_PSIZE_MOD;
198                 }
199                 fp.fp_psize = psize;
200
201
202                 switch (wscale_mod) {
203                 case T_DC:
204                         fp.fp_flags |= PF_OSFP_WSCALE_DC;
205                         break;
206                 case T_MOD:
207                         fp.fp_flags |= PF_OSFP_WSCALE_MOD;
208                 }
209                 fp.fp_wscale = wscale;
210
211                 switch (mss_mod) {
212                 case T_DC:
213                         fp.fp_flags |= PF_OSFP_MSS_DC;
214                         break;
215                 case T_MOD:
216                         fp.fp_flags |= PF_OSFP_MSS_MOD;
217                         break;
218                 }
219                 fp.fp_mss = mss;
220
221                 fp.fp_tcpopts = packed_tcpopts;
222                 fp.fp_optcnt = optcnt;
223                 if (ts0)
224                         fp.fp_flags |= PF_OSFP_TS0;
225
226                 if (class[0] == '@')
227                         fp.fp_os.fp_enflags |= PF_OSFP_GENERIC;
228                 if (class[0] == '*')
229                         fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL;
230
231                 if (class[0] == '@' || class[0] == '*')
232                         strlcpy(fp.fp_os.fp_class_nm, class + 1,
233                             sizeof(fp.fp_os.fp_class_nm));
234                 else
235                         strlcpy(fp.fp_os.fp_class_nm, class,
236                             sizeof(fp.fp_os.fp_class_nm));
237                 strlcpy(fp.fp_os.fp_version_nm, version,
238                     sizeof(fp.fp_os.fp_version_nm));
239                 strlcpy(fp.fp_os.fp_subtype_nm, subtype,
240                     sizeof(fp.fp_os.fp_subtype_nm));
241
242                 add_fingerprint(dev, opts, &fp);
243         }
244
245         if (class)
246                 free(class);
247         if (version)
248                 free(version);
249         if (subtype)
250                 free(subtype);
251         if (desc)
252                 free(desc);
253
254         fclose(in);
255
256         if (opts & PF_OPT_VERBOSE2)
257                 printf("Loaded %d passive OS fingerprints\n",
258                     fingerprint_count);
259         return (0);
260 }
261
262 /* flush the kernel's fingerprints */
263 void
264 pfctl_clear_fingerprints(int dev, int opts)
265 {
266         if (ioctl(dev, DIOCOSFPFLUSH))
267                 err(1, "DIOCOSFPFLUSH");
268 }
269
270 /* flush pfctl's view of the fingerprints */
271 void
272 pfctl_flush_my_fingerprints(struct name_list *list)
273 {
274         struct name_entry *nm;
275
276         while ((nm = LIST_FIRST(list)) != NULL) {
277                 LIST_REMOVE(nm, nm_entry);
278                 pfctl_flush_my_fingerprints(&nm->nm_sublist);
279                 free(nm);
280         }
281         fingerprint_count = 0;
282         class_count = 0;
283 }
284
285 /* Fetch the active fingerprints from the kernel */
286 int
287 pfctl_load_fingerprints(int dev, int opts)
288 {
289         struct pf_osfp_ioctl io;
290         int i;
291
292         pfctl_flush_my_fingerprints(&classes);
293
294         for (i = 0; i >= 0; i++) {
295                 memset(&io, 0, sizeof(io));
296                 io.fp_getnum = i;
297                 if (ioctl(dev, DIOCOSFPGET, &io)) {
298                         if (errno == EBUSY)
299                                 break;
300                         warn("DIOCOSFPGET");
301                         return (1);
302                 }
303                 import_fingerprint(&io);
304         }
305         return (0);
306 }
307
308 /* List the fingerprints */
309 void
310 pfctl_show_fingerprints(int opts)
311 {
312         if (LIST_FIRST(&classes) != NULL) {
313                 if (opts & PF_OPT_SHOWALL) {
314                         pfctl_print_title("OS FINGERPRINTS:");
315                         printf("%u fingerprints loaded\n", fingerprint_count);
316                 } else {
317                         printf("Class\tVersion\tSubtype(subversion)\n");
318                         printf("-----\t-------\t-------------------\n");
319                         sort_name_list(opts, &classes);
320                         print_name_list(opts, &classes, "");
321                 }
322         }
323 }
324
325 /* Lookup a fingerprint */
326 pf_osfp_t
327 pfctl_get_fingerprint(const char *name)
328 {
329         struct name_entry *nm, *class_nm, *version_nm, *subtype_nm;
330         pf_osfp_t ret = PF_OSFP_NOMATCH;
331         int class, version, subtype;
332         int unp_class, unp_version, unp_subtype;
333         int wr_len, version_len, subtype_len;
334         char *ptr, *wr_name;
335
336         if (strcasecmp(name, "unknown") == 0)
337                 return (PF_OSFP_UNKNOWN);
338
339         /* Try most likely no version and no subtype */
340         if ((nm = lookup_name_list(&classes, name))) {
341                 class = nm->nm_num;
342                 version = PF_OSFP_ANY;
343                 subtype = PF_OSFP_ANY;
344                 goto found;
345         } else {
346
347                 /* Chop it up into class/version/subtype */
348
349                 if ((wr_name = strdup(name)) == NULL)
350                         err(1, "malloc");
351                 if ((ptr = strchr(wr_name, ' ')) == NULL) {
352                         free(wr_name);
353                         return (PF_OSFP_NOMATCH);
354                 }
355                 *ptr++ = '\0';
356
357                 /* The class is easy to find since it is delimited by a space */
358                 if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) {
359                         free(wr_name);
360                         return (PF_OSFP_NOMATCH);
361                 }
362                 class = class_nm->nm_num;
363
364                 /* Try no subtype */
365                 if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr)))
366                 {
367                         version = version_nm->nm_num;
368                         subtype = PF_OSFP_ANY;
369                         free(wr_name);
370                         goto found;
371                 }
372
373
374                 /*
375                  * There must be a version and a subtype.
376                  * We'll do some fuzzy matching to pick up things like:
377                  *   Linux 2.2.14 (version=2.2 subtype=14)
378                  *   FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE)
379                  *   Windows 2000 SP2   (version=2000 subtype=SP2)
380                  */
381 #define CONNECTOR(x)    ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-')
382                 wr_len = strlen(ptr);
383                 LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) {
384                         version_len = strlen(version_nm->nm_name);
385                         if (wr_len < version_len + 2 ||
386                             !CONNECTOR(ptr[version_len]))
387                                 continue;
388                         /* first part of the string must be version */
389                         if (strncasecmp(ptr, version_nm->nm_name,
390                             version_len))
391                                 continue;
392
393                         LIST_FOREACH(subtype_nm, &version_nm->nm_sublist,
394                             nm_entry) {
395                                 subtype_len = strlen(subtype_nm->nm_name);
396                                 if (wr_len != version_len + subtype_len + 1)
397                                         continue;
398
399                                 /* last part of the string must be subtype */
400                                 if (strcasecmp(&ptr[version_len+1],
401                                     subtype_nm->nm_name) != 0)
402                                         continue;
403
404                                 /* Found it!! */
405                                 version = version_nm->nm_num;
406                                 subtype = subtype_nm->nm_num;
407                                 free(wr_name);
408                                 goto found;
409                         }
410                 }
411
412                 free(wr_name);
413                 return (PF_OSFP_NOMATCH);
414         }
415
416 found:
417         PF_OSFP_PACK(ret, class, version, subtype);
418         if (ret != PF_OSFP_NOMATCH) {
419                 PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype);
420                 if (class != unp_class) {
421                         fprintf(stderr, "warning: fingerprint table overflowed "
422                             "classes\n");
423                         return (PF_OSFP_NOMATCH);
424                 }
425                 if (version != unp_version) {
426                         fprintf(stderr, "warning: fingerprint table overflowed "
427                             "versions\n");
428                         return (PF_OSFP_NOMATCH);
429                 }
430                 if (subtype != unp_subtype) {
431                         fprintf(stderr, "warning: fingerprint table overflowed "
432                             "subtypes\n");
433                         return (PF_OSFP_NOMATCH);
434                 }
435         }
436         if (ret == PF_OSFP_ANY) {
437                 /* should never happen */
438                 fprintf(stderr, "warning: fingerprint packed to 'any'\n");
439                 return (PF_OSFP_NOMATCH);
440         }
441
442         return (ret);
443 }
444
445 /* Lookup a fingerprint name by ID */
446 char *
447 pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len)
448 {
449         int class, version, subtype;
450         struct name_list *list;
451         struct name_entry *nm;
452
453         char *class_name, *version_name, *subtype_name;
454         class_name = version_name = subtype_name = NULL;
455
456         if (fp == PF_OSFP_UNKNOWN) {
457                 strlcpy(buf, "unknown", len);
458                 return (buf);
459         }
460         if (fp == PF_OSFP_ANY) {
461                 strlcpy(buf, "any", len);
462                 return (buf);
463         }
464
465         PF_OSFP_UNPACK(fp, class, version, subtype);
466         if (class >= (1 << _FP_CLASS_BITS) ||
467             version >= (1 << _FP_VERSION_BITS) ||
468             subtype >= (1 << _FP_SUBTYPE_BITS)) {
469                 warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp);
470                 strlcpy(buf, "nomatch", len);
471                 return (buf);
472         }
473
474         LIST_FOREACH(nm, &classes, nm_entry) {
475                 if (nm->nm_num == class) {
476                         class_name = nm->nm_name;
477                         if (version == PF_OSFP_ANY)
478                                 goto found;
479                         list = &nm->nm_sublist;
480                         LIST_FOREACH(nm, list, nm_entry) {
481                                 if (nm->nm_num == version) {
482                                         version_name = nm->nm_name;
483                                         if (subtype == PF_OSFP_ANY)
484                                                 goto found;
485                                         list = &nm->nm_sublist;
486                                         LIST_FOREACH(nm, list, nm_entry) {
487                                                 if (nm->nm_num == subtype) {
488                                                         subtype_name =
489                                                             nm->nm_name;
490                                                         goto found;
491                                                 }
492                                         } /* foreach subtype */
493                                         strlcpy(buf, "nomatch", len);
494                                         return (buf);
495                                 }
496                         } /* foreach version */
497                         strlcpy(buf, "nomatch", len);
498                         return (buf);
499                 }
500         } /* foreach class */
501
502         strlcpy(buf, "nomatch", len);
503         return (buf);
504
505 found:
506         snprintf(buf, len, "%s", class_name);
507         if (version_name) {
508                 strlcat(buf, " ", len);
509                 strlcat(buf, version_name, len);
510                 if (subtype_name) {
511                         if (strchr(version_name, ' '))
512                                 strlcat(buf, " ", len);
513                         else if (strchr(version_name, '.') &&
514                             isdigit(*subtype_name))
515                                 strlcat(buf, ".", len);
516                         else
517                                 strlcat(buf, " ", len);
518                         strlcat(buf, subtype_name, len);
519                 }
520         }
521         return (buf);
522 }
523
524 /* lookup a name in a list */
525 struct name_entry *
526 lookup_name_list(struct name_list *list, const char *name)
527 {
528         struct name_entry *nm;
529         LIST_FOREACH(nm, list, nm_entry)
530                 if (strcasecmp(name, nm->nm_name) == 0)
531                         return (nm);
532
533         return (NULL);
534 }
535
536
537 void
538 add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp)
539 {
540         struct pf_osfp_ioctl fptmp;
541         struct name_entry *nm_class, *nm_version, *nm_subtype;
542         int class, version, subtype;
543
544 /* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */
545 #define EXPAND(field) do {                                              \
546         int _dot = -1, _start = -1, _end = -1, _i = 0;                  \
547         /* pick major version out of #.# */                             \
548         if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') {         \
549                 _dot = fp->field[_i] - '0';                             \
550                 _i += 2;                                                \
551         }                                                               \
552         if (isdigit(fp->field[_i]))                                     \
553                 _start = fp->field[_i++] - '0';                         \
554         else                                                            \
555                 break;                                                  \
556         if (isdigit(fp->field[_i]))                                     \
557                 _start = (_start * 10) + fp->field[_i++] - '0';         \
558         if (fp->field[_i++] != '-')                                     \
559                 break;                                                  \
560         if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' &&         \
561             fp->field[_i] - '0' == _dot)                                \
562                 _i += 2;                                                \
563         else if (_dot != -1)                                            \
564                 break;                                                  \
565         if (isdigit(fp->field[_i]))                                     \
566                 _end = fp->field[_i++] - '0';                           \
567         else                                                            \
568                 break;                                                  \
569         if (isdigit(fp->field[_i]))                                     \
570                 _end = (_end * 10) + fp->field[_i++] - '0';             \
571         if (isdigit(fp->field[_i]))                                     \
572                 _end = (_end * 10) + fp->field[_i++] - '0';             \
573         if (fp->field[_i] != '\0')                                      \
574                 break;                                                  \
575         memcpy(&fptmp, fp, sizeof(fptmp));                              \
576         for (;_start <= _end; _start++) {                               \
577                 memset(fptmp.field, 0, sizeof(fptmp.field));            \
578                 fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED;             \
579                 if (_dot == -1)                                         \
580                         snprintf(fptmp.field, sizeof(fptmp.field),      \
581                             "%d", _start);                              \
582                     else                                                \
583                         snprintf(fptmp.field, sizeof(fptmp.field),      \
584                             "%d.%d", _dot, _start);                     \
585                 add_fingerprint(dev, opts, &fptmp);                     \
586         }                                                               \
587 } while(0)
588
589         /* We allow "#-#" as a version or subtype and we'll expand it */
590         EXPAND(fp_os.fp_version_nm);
591         EXPAND(fp_os.fp_subtype_nm);
592
593         if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0)
594                 errx(1, "fingerprint class \"nomatch\" is reserved");
595
596         version = PF_OSFP_ANY;
597         subtype = PF_OSFP_ANY;
598
599         nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
600         if (nm_class->nm_num == 0)
601                 nm_class->nm_num = ++class_count;
602         class = nm_class->nm_num;
603
604         nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
605             fp->fp_os.fp_version_nm);
606         if (nm_version) {
607                 if (nm_version->nm_num == 0)
608                         nm_version->nm_num = ++nm_class->nm_sublist_num;
609                 version = nm_version->nm_num;
610                 nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
611                     fp->fp_os.fp_subtype_nm);
612                 if (nm_subtype) {
613                         if (nm_subtype->nm_num == 0)
614                                 nm_subtype->nm_num =
615                                     ++nm_version->nm_sublist_num;
616                         subtype = nm_subtype->nm_num;
617                 }
618         }
619
620
621         DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype,
622             print_ioctl(fp));
623
624         PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype);
625         fingerprint_count++;
626
627 #ifdef FAKE_PF_KERNEL
628         /* Linked to the sys/net/pf_osfp.c.  Call pf_osfp_add() */
629         if ((errno = pf_osfp_add(fp)))
630 #else
631         if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp))
632 #endif /* FAKE_PF_KERNEL */
633         {
634                 if (errno == EEXIST) {
635                         warn("Duplicate signature for %s %s %s",
636                                 fp->fp_os.fp_class_nm,
637                                 fp->fp_os.fp_version_nm,
638                                 fp->fp_os.fp_subtype_nm);
639
640                 } else {
641                         err(1, "DIOCOSFPADD");
642                 }
643         }
644 }
645
646 /* import a fingerprint from the kernel */
647 void
648 import_fingerprint(struct pf_osfp_ioctl *fp)
649 {
650         struct name_entry *nm_class, *nm_version, *nm_subtype;
651         int class, version, subtype;
652
653         PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype);
654
655         nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
656         if (nm_class->nm_num == 0) {
657                 nm_class->nm_num = class;
658                 class_count = MAX(class_count, class);
659         }
660
661         nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
662             fp->fp_os.fp_version_nm);
663         if (nm_version) {
664                 if (nm_version->nm_num == 0) {
665                         nm_version->nm_num = version;
666                         nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num,
667                             version);
668                 }
669                 nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
670                     fp->fp_os.fp_subtype_nm);
671                 if (nm_subtype) {
672                         if (nm_subtype->nm_num == 0) {
673                                 nm_subtype->nm_num = subtype;
674                                 nm_version->nm_sublist_num =
675                                     MAX(nm_version->nm_sublist_num, subtype);
676                         }
677                 }
678         }
679
680
681         fingerprint_count++;
682         DEBUG(fp, "import signature %d:%d:%d", class, version, subtype);
683 }
684
685 /* Find an entry for a fingerprints class/version/subtype */
686 struct name_entry *
687 fingerprint_name_entry(struct name_list *list, char *name)
688 {
689         struct name_entry *nm_entry;
690
691         if (name == NULL || strlen(name) == 0)
692                 return (NULL);
693
694         LIST_FOREACH(nm_entry, list, nm_entry) {
695                 if (strcasecmp(nm_entry->nm_name, name) == 0) {
696                         /* We'll move this to the front of the list later */
697                         LIST_REMOVE(nm_entry, nm_entry);
698                         break;
699                 }
700         }
701         if (nm_entry == NULL) {
702                 nm_entry = calloc(1, sizeof(*nm_entry));
703                 if (nm_entry == NULL)
704                         err(1, "calloc");
705                 LIST_INIT(&nm_entry->nm_sublist);
706                 strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name));
707         }
708         LIST_INSERT_HEAD(list, nm_entry, nm_entry);
709         return (nm_entry);
710 }
711
712
713 void
714 print_name_list(int opts, struct name_list *nml, const char *prefix)
715 {
716         char newprefix[32];
717         struct name_entry *nm;
718
719         LIST_FOREACH(nm, nml, nm_entry) {
720                 snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix,
721                     nm->nm_name);
722                 printf("%s\n", newprefix);
723                 print_name_list(opts, &nm->nm_sublist, newprefix);
724         }
725 }
726
727 void
728 sort_name_list(int opts, struct name_list *nml)
729 {
730         struct name_list new;
731         struct name_entry *nm, *nmsearch, *nmlast;
732
733         /* yes yes, it's a very slow sort.  so sue me */
734
735         LIST_INIT(&new);
736
737         while ((nm = LIST_FIRST(nml)) != NULL) {
738                 LIST_REMOVE(nm, nm_entry);
739                 nmlast = NULL;
740                 LIST_FOREACH(nmsearch, &new, nm_entry) {
741                         if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) {
742                                 LIST_INSERT_BEFORE(nmsearch, nm, nm_entry);
743                                 break;
744                         }
745                         nmlast = nmsearch;
746                 }
747                 if (nmsearch == NULL) {
748                         if (nmlast)
749                                 LIST_INSERT_AFTER(nmlast, nm, nm_entry);
750                         else
751                                 LIST_INSERT_HEAD(&new, nm, nm_entry);
752                 }
753
754                 sort_name_list(opts, &nm->nm_sublist);
755         }
756         nmlast = NULL;
757         while ((nm = LIST_FIRST(&new)) != NULL) {
758                 LIST_REMOVE(nm, nm_entry);
759                 if (nmlast == NULL)
760                         LIST_INSERT_HEAD(nml, nm, nm_entry);
761                 else
762                         LIST_INSERT_AFTER(nmlast, nm, nm_entry);
763                 nmlast = nm;
764         }
765         return;
766 }
767
768 /* parse the next integer in a formatted config file line */
769 int
770 get_int(char **line, size_t *len, int *var, int *mod,
771     const char *name, int flags, int max, const char *filename, int lineno)
772 {
773         int fieldlen, i;
774         char *field;
775         long val = 0;
776
777         if (mod)
778                 *mod = 0;
779         *var = 0;
780
781         field = get_field(line, len, &fieldlen);
782         if (field == NULL)
783                 return (1);
784         if (fieldlen == 0) {
785                 fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name);
786                 return (1);
787         }
788
789         i = 0;
790         if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*')
791             && fieldlen >= 1) {
792                 switch (*field) {
793                 case 'S':
794                         if (mod && (flags & T_MSS))
795                                 *mod = T_MSS;
796                         if (fieldlen == 1)
797                                 return (0);
798                         break;
799                 case 'T':
800                         if (mod && (flags & T_MTU))
801                                 *mod = T_MTU;
802                         if (fieldlen == 1)
803                                 return (0);
804                         break;
805                 case '*':
806                         if (fieldlen != 1) {
807                                 fprintf(stderr, "%s:%d long '%c' %s\n",
808                                     filename, lineno, *field, name);
809                                 return (1);
810                         }
811                         if (mod && (flags & T_DC)) {
812                                 *mod = T_DC;
813                                 return (0);
814                         }
815                 case '%':
816                         if (mod && (flags & T_MOD))
817                                 *mod = T_MOD;
818                         if (fieldlen == 1) {
819                                 fprintf(stderr, "%s:%d modulus %s must have a "
820                                     "value\n", filename, lineno, name);
821                                 return (1);
822                         }
823                         break;
824                 }
825                 if (mod == NULL || *mod == 0) {
826                         fprintf(stderr, "%s:%d does not allow %c' %s\n",
827                             filename, lineno, *field, name);
828                         return (1);
829                 }
830                 i++;
831         }
832
833         for (; i < fieldlen; i++) {
834                 if (field[i] < '0' || field[i] > '9') {
835                         fprintf(stderr, "%s:%d non-digit character in %s\n",
836                             filename, lineno, name);
837                         return (1);
838                 }
839                 val = val * 10 + field[i] - '0';
840                 if (val < 0) {
841                         fprintf(stderr, "%s:%d %s overflowed\n", filename,
842                             lineno, name);
843                         return (1);
844                 }
845         }
846
847         if (val > max) {
848                 fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno,
849                     name, val, max);
850                 return (1);
851         }
852         *var = (int)val;
853
854         return (0);
855 }
856
857 /* parse the next string in a formatted config file line */
858 int
859 get_str(char **line, size_t *len, char **v, const char *name, int minlen,
860     const char *filename, int lineno)
861 {
862         int fieldlen;
863         char *ptr;
864
865         ptr = get_field(line, len, &fieldlen);
866         if (ptr == NULL)
867                 return (1);
868         if (fieldlen < minlen) {
869                 fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name);
870                 return (1);
871         }
872         if ((*v = malloc(fieldlen + 1)) == NULL) {
873                 perror("malloc()");
874                 return (1);
875         }
876         memcpy(*v, ptr, fieldlen);
877         (*v)[fieldlen] = '\0';
878
879         return (0);
880 }
881
882 /* Parse out the TCP opts */
883 int
884 get_tcpopts(const char *filename, int lineno, const char *tcpopts,
885     pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale,
886     int *wscale_mod, int *ts0)
887 {
888         int i, opt;
889
890         *packed = 0;
891         *optcnt = 0;
892         *wscale = 0;
893         *wscale_mod = T_DC;
894         *mss = 0;
895         *mss_mod = T_DC;
896         *ts0 = 0;
897         if (strcmp(tcpopts, ".") == 0)
898                 return (0);
899
900         for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) {
901                 switch ((opt = toupper(tcpopts[i++]))) {
902                 case 'N':       /* FALLTHROUGH */
903                 case 'S':
904                         *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
905                             (opt == 'N' ? PF_OSFP_TCPOPT_NOP :
906                             PF_OSFP_TCPOPT_SACK);
907                         break;
908                 case 'W':       /* FALLTHROUGH */
909                 case 'M': {
910                         int *this_mod, *this;
911
912                         if (opt == 'W') {
913                                 this = wscale;
914                                 this_mod = wscale_mod;
915                         } else {
916                                 this = mss;
917                                 this_mod = mss_mod;
918                         }
919                         *this = 0;
920                         *this_mod = 0;
921
922                         *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
923                             (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE :
924                             PF_OSFP_TCPOPT_MSS);
925                         if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' ||
926                             tcpopts[i + 1] == ',')) {
927                                 *this_mod = T_DC;
928                                 i++;
929                                 break;
930                         }
931
932                         if (tcpopts[i] == '%') {
933                                 *this_mod = T_MOD;
934                                 i++;
935                         }
936                         do {
937                                 if (!isdigit(tcpopts[i])) {
938                                         fprintf(stderr, "%s:%d unknown "
939                                             "character '%c' in %c TCP opt\n",
940                                             filename, lineno, tcpopts[i], opt);
941                                         return (1);
942                                 }
943                                 *this = (*this * 10) + tcpopts[i++] - '0';
944                         } while(tcpopts[i] != ',' && tcpopts[i] != '\0');
945                         break;
946                 }
947                 case 'T':
948                         if (tcpopts[i] == '0') {
949                                 *ts0 = 1;
950                                 i++;
951                         }
952                         *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
953                             PF_OSFP_TCPOPT_TS;
954                         break;
955                 }
956                 (*optcnt) ++;
957                 if (tcpopts[i] == '\0')
958                         break;
959                 if (tcpopts[i] != ',') {
960                         fprintf(stderr, "%s:%d unknown option to %c TCP opt\n",
961                             filename, lineno, opt);
962                         return (1);
963                 }
964                 i++;
965         }
966
967         return (0);
968 }
969
970 /* rip the next field ouf of a formatted config file line */
971 char *
972 get_field(char **line, size_t *len, int *fieldlen)
973 {
974         char *ret, *ptr = *line;
975         size_t plen = *len;
976
977
978         while (plen && isspace(*ptr)) {
979                 plen--;
980                 ptr++;
981         }
982         ret = ptr;
983         *fieldlen = 0;
984
985         for (; plen > 0 && *ptr != ':'; plen--, ptr++)
986                 (*fieldlen)++;
987         if (plen) {
988                 *line = ptr + 1;
989                 *len = plen - 1;
990         } else {
991                 *len = 0;
992         }
993         while (*fieldlen && isspace(ret[*fieldlen - 1]))
994                 (*fieldlen)--;
995         return (ret);
996 }
997
998
999 const char *
1000 print_ioctl(struct pf_osfp_ioctl *fp)
1001 {
1002         static char buf[1024];
1003         char tmp[32];
1004         int i, opt;
1005
1006         *buf = '\0';
1007         if (fp->fp_flags & PF_OSFP_WSIZE_DC)
1008                 strlcat(buf, "*", sizeof(buf));
1009         else if (fp->fp_flags & PF_OSFP_WSIZE_MSS)
1010                 strlcat(buf, "S", sizeof(buf));
1011         else if (fp->fp_flags & PF_OSFP_WSIZE_MTU)
1012                 strlcat(buf, "T", sizeof(buf));
1013         else {
1014                 if (fp->fp_flags & PF_OSFP_WSIZE_MOD)
1015                         strlcat(buf, "%", sizeof(buf));
1016                 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize);
1017                 strlcat(buf, tmp, sizeof(buf));
1018         }
1019         strlcat(buf, ":", sizeof(buf));
1020
1021         snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl);
1022         strlcat(buf, tmp, sizeof(buf));
1023         strlcat(buf, ":", sizeof(buf));
1024
1025         if (fp->fp_flags & PF_OSFP_DF)
1026                 strlcat(buf, "1", sizeof(buf));
1027         else
1028                 strlcat(buf, "0", sizeof(buf));
1029         strlcat(buf, ":", sizeof(buf));
1030
1031         if (fp->fp_flags & PF_OSFP_PSIZE_DC)
1032                 strlcat(buf, "*", sizeof(buf));
1033         else {
1034                 if (fp->fp_flags & PF_OSFP_PSIZE_MOD)
1035                         strlcat(buf, "%", sizeof(buf));
1036                 snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize);
1037                 strlcat(buf, tmp, sizeof(buf));
1038         }
1039         strlcat(buf, ":", sizeof(buf));
1040
1041         if (fp->fp_optcnt == 0)
1042                 strlcat(buf, ".", sizeof(buf));
1043         for (i = fp->fp_optcnt - 1; i >= 0; i--) {
1044                 opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS);
1045                 opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1;
1046                 switch (opt) {
1047                 case PF_OSFP_TCPOPT_NOP:
1048                         strlcat(buf, "N", sizeof(buf));
1049                         break;
1050                 case PF_OSFP_TCPOPT_SACK:
1051                         strlcat(buf, "S", sizeof(buf));
1052                         break;
1053                 case PF_OSFP_TCPOPT_TS:
1054                         strlcat(buf, "T", sizeof(buf));
1055                         if (fp->fp_flags & PF_OSFP_TS0)
1056                                 strlcat(buf, "0", sizeof(buf));
1057                         break;
1058                 case PF_OSFP_TCPOPT_MSS:
1059                         strlcat(buf, "M", sizeof(buf));
1060                         if (fp->fp_flags & PF_OSFP_MSS_DC)
1061                                 strlcat(buf, "*", sizeof(buf));
1062                         else {
1063                                 if (fp->fp_flags & PF_OSFP_MSS_MOD)
1064                                         strlcat(buf, "%", sizeof(buf));
1065                                 snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss);
1066                                 strlcat(buf, tmp, sizeof(buf));
1067                         }
1068                         break;
1069                 case PF_OSFP_TCPOPT_WSCALE:
1070                         strlcat(buf, "W", sizeof(buf));
1071                         if (fp->fp_flags & PF_OSFP_WSCALE_DC)
1072                                 strlcat(buf, "*", sizeof(buf));
1073                         else {
1074                                 if (fp->fp_flags & PF_OSFP_WSCALE_MOD)
1075                                         strlcat(buf, "%", sizeof(buf));
1076                                 snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale);
1077                                 strlcat(buf, tmp, sizeof(buf));
1078                         }
1079                         break;
1080                 }
1081
1082                 if (i != 0)
1083                         strlcat(buf, ",", sizeof(buf));
1084         }
1085         strlcat(buf, ":", sizeof(buf));
1086
1087         strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf));
1088         strlcat(buf, ":", sizeof(buf));
1089         strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf));
1090         strlcat(buf, ":", sizeof(buf));
1091         strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf));
1092         strlcat(buf, ":", sizeof(buf));
1093
1094         snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt,
1095             (long long int)fp->fp_tcpopts);
1096         strlcat(buf, tmp, sizeof(buf));
1097
1098         return (buf);
1099 }