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