Annotation of src/usr.bin/systat/pftop.c, Revision 1.47
1.47 ! jsg 1: /* $OpenBSD: pftop.c,v 1.46 2023/07/04 11:34:19 sashan Exp $ */
1.1 canacar 2: /*
3: * Copyright (c) 2001, 2007 Can Erkin Acar
4: * Copyright (c) 2001 Daniel Hartmeier
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: *
11: * - Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * - Redistributions in binary form must reproduce the above
14: * copyright notice, this list of conditions and the following
15: * disclaimer in the documentation and/or other materials provided
16: * with the distribution.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21: * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22: * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29: * POSSIBILITY OF SUCH DAMAGE.
30: *
31: */
32:
33: #include <sys/types.h>
34: #include <sys/ioctl.h>
35: #include <sys/socket.h>
36:
37: #include <net/if.h>
38: #include <netinet/in.h>
1.8 mcbride 39: #include <netinet/tcp.h>
1.1 canacar 40: #include <netinet/tcp_fsm.h>
41: #include <net/pfvar.h>
42: #include <arpa/inet.h>
43:
1.22 henning 44: #include <net/hfsc.h>
45:
1.1 canacar 46: #include <ctype.h>
47: #include <curses.h>
48: #include <err.h>
49: #include <errno.h>
50: #include <fcntl.h>
51: #include <netdb.h>
52: #include <signal.h>
53: #include <stdio.h>
54: #include <stdlib.h>
55: #include <string.h>
56: #include <unistd.h>
1.30 deraadt 57: #include <limits.h>
1.1 canacar 58: #include <stdarg.h>
59:
1.6 canacar 60: #include "systat.h"
1.1 canacar 61: #include "engine.h"
62: #include "cache.h"
63:
64: extern const char *tcpstates[];
65:
66: #define MIN_NUM_STATES 1024
67: #define NUM_STATE_INC 1024
68:
69: #define DEFAULT_CACHE_SIZE 10000
70:
71: /* XXX must also check type before use */
72: #define PT_ADDR(x) (&(x)->addr.v.a.addr)
73:
74: /* XXX must also check type before use */
75: #define PT_MASK(x) (&(x)->addr.v.a.mask)
76:
77: #define PT_NOROUTE(x) ((x)->addr.type == PF_ADDR_NOROUTE)
78:
79: /* view management */
80: int select_states(void);
81: int read_states(void);
82: void sort_states(void);
83: void print_states(void);
84:
85: int select_rules(void);
86: int read_rules(void);
87: void print_rules(void);
88:
89: int select_queues(void);
90: int read_queues(void);
91: void print_queues(void);
92:
1.7 canacar 93: void update_cache(void);
94:
1.1 canacar 95: /* qsort callbacks */
96: int sort_size_callback(const void *s1, const void *s2);
97: int sort_exp_callback(const void *s1, const void *s2);
98: int sort_pkt_callback(const void *s1, const void *s2);
99: int sort_age_callback(const void *s1, const void *s2);
100: int sort_sa_callback(const void *s1, const void *s2);
101: int sort_sp_callback(const void *s1, const void *s2);
102: int sort_da_callback(const void *s1, const void *s2);
103: int sort_dp_callback(const void *s1, const void *s2);
104: int sort_rate_callback(const void *s1, const void *s2);
105: int sort_peak_callback(const void *s1, const void *s2);
106: int pf_dev = -1;
107:
108: struct sc_ent **state_cache = NULL;
1.4 canacar 109: struct pfsync_state *state_buf = NULL;
1.43 bluhm 110: size_t state_buf_len = 0;
111: size_t *state_ord = NULL;
112: size_t num_states = 0;
113: size_t num_states_all = 0;
1.1 canacar 114: u_int32_t num_rules = 0;
115: u_int32_t num_queues = 0;
116: int cachestates = 0;
117:
118: char *filter_string = NULL;
119:
120: #define MIN_LABEL_SIZE 5
121: #define ANCHOR_FLD_SIZE 12
122:
123: /* Define fields */
124: field_def fields[] = {
125: {"SRC", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
126: {"DEST", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
127: {"GW", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
128: {"STATE", 5, 23, 18, FLD_ALIGN_COLUMN, -1, 0, 0, 0},
129: {"AGE", 5, 9, 4, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
130: {"EXP", 5, 9, 4, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
131: {"PR ", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
132: {"DIR", 1, 3, 2, FLD_ALIGN_CENTER, -1, 0, 0, 0},
133: {"PKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
134: {"BYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
135: {"RULE", 2, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
136: {"LABEL", MIN_LABEL_SIZE, MIN_LABEL_SIZE, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
137: {"STATES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
138: {"EVAL", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
139: {"ACTION", 1, 8, 4, FLD_ALIGN_LEFT, -1, 0, 0, 0},
140: {"LOG", 1, 3, 2, FLD_ALIGN_LEFT, -1, 0, 0, 0},
141: {"QUICK", 1, 1, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
142: {"KS", 1, 1, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.35 jasper 143: {"IF", 4, 7, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.1 canacar 144: {"INFO", 40, 80, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.4 canacar 145: {"MAX", 3, 5, 2, FLD_ALIGN_RIGHT, -1, 0, 0},
1.1 canacar 146: {"RATE", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
147: {"AVG", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
148: {"PEAK", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.4 canacar 149: {"ANCHOR", 6, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0},
1.1 canacar 150: {"QUEUE", 15, 30, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.39 mikeb 151: {"BW/FL", 4, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.1 canacar 152: {"SCH", 3, 4, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
153: {"DROP_P", 6, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
154: {"DROP_B", 6, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
155: {"QLEN", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
156: {"BORROW", 4, 6, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
157: {"SUSPENDS", 4, 6, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
158: {"P/S", 3, 7, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
159: {"B/S", 4, 7, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}
160: };
161:
162:
163: /* for states */
1.18 jasper 164: #define FLD_SRC FIELD_ADDR(fields,0)
165: #define FLD_DEST FIELD_ADDR(fields,1)
166: #define FLD_GW FIELD_ADDR(fields,2)
167: #define FLD_STATE FIELD_ADDR(fields,3)
168: #define FLD_AGE FIELD_ADDR(fields,4)
169: #define FLD_EXP FIELD_ADDR(fields,5)
1.1 canacar 170: /* common */
1.18 jasper 171: #define FLD_PROTO FIELD_ADDR(fields,6)
172: #define FLD_DIR FIELD_ADDR(fields,7)
173: #define FLD_PKTS FIELD_ADDR(fields,8)
174: #define FLD_BYTES FIELD_ADDR(fields,9)
175: #define FLD_RULE FIELD_ADDR(fields,10)
1.1 canacar 176: /* for rules */
1.18 jasper 177: #define FLD_LABEL FIELD_ADDR(fields,11)
178: #define FLD_STATS FIELD_ADDR(fields,12)
179: #define FLD_EVAL FIELD_ADDR(fields,13)
180: #define FLD_ACTION FIELD_ADDR(fields,14)
181: #define FLD_LOG FIELD_ADDR(fields,15)
182: #define FLD_QUICK FIELD_ADDR(fields,16)
183: #define FLD_KST FIELD_ADDR(fields,17)
184: #define FLD_IF FIELD_ADDR(fields,18)
185: #define FLD_RINFO FIELD_ADDR(fields,19)
186: #define FLD_STMAX FIELD_ADDR(fields,20)
1.1 canacar 187: /* other */
1.18 jasper 188: #define FLD_SI FIELD_ADDR(fields,21) /* instantaneous speed */
189: #define FLD_SA FIELD_ADDR(fields,22) /* average speed */
190: #define FLD_SP FIELD_ADDR(fields,23) /* peak speed */
191: #define FLD_ANCHOR FIELD_ADDR(fields,24)
1.1 canacar 192: /* for queues */
1.18 jasper 193: #define FLD_QUEUE FIELD_ADDR(fields,25)
194: #define FLD_BANDW FIELD_ADDR(fields,26)
195: #define FLD_SCHED FIELD_ADDR(fields,27)
1.37 mikeb 196: #define FLD_DROPP FIELD_ADDR(fields,28)
197: #define FLD_DROPB FIELD_ADDR(fields,29)
198: #define FLD_QLEN FIELD_ADDR(fields,30)
199: #define FLD_BORR FIELD_ADDR(fields,31)
200: #define FLD_SUSP FIELD_ADDR(fields,32)
201: #define FLD_PKTSPS FIELD_ADDR(fields,33)
202: #define FLD_BYTESPS FIELD_ADDR(fields,34)
1.1 canacar 203:
204: /* Define views */
205: field_def *view0[] = {
206: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_STATE,
207: FLD_AGE, FLD_EXP, FLD_PKTS, FLD_BYTES, NULL
208: };
209:
210: field_def *view1[] = {
211: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_GW, FLD_STATE, FLD_AGE,
212: FLD_EXP, FLD_PKTS, FLD_BYTES, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, NULL
213: };
214:
215: field_def *view2[] = {
216: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_STATE, FLD_AGE, FLD_EXP,
217: FLD_PKTS, FLD_BYTES, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
218: };
219:
220: field_def *view3[] = {
221: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_AGE, FLD_EXP, FLD_PKTS,
222: FLD_BYTES, FLD_STATE, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
223: };
224:
225: field_def *view4[] = {
226: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_PKTS, FLD_BYTES, FLD_STATE,
227: FLD_AGE, FLD_EXP, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
228: };
229:
230: field_def *view5[] = {
231: FLD_RULE, FLD_ANCHOR, FLD_ACTION, FLD_DIR, FLD_LOG, FLD_QUICK, FLD_IF,
232: FLD_PROTO, FLD_KST, FLD_PKTS, FLD_BYTES, FLD_STATS, FLD_STMAX,
233: FLD_RINFO, NULL
234: };
235:
236: field_def *view6[] = {
237: FLD_RULE, FLD_LABEL, FLD_PKTS, FLD_BYTES, FLD_STATS, FLD_STMAX,
238: FLD_ACTION, FLD_DIR, FLD_LOG, FLD_QUICK, FLD_IF, FLD_PROTO,
239: FLD_ANCHOR, FLD_KST, NULL
240: };
241:
242: field_def *view7[] = {
243: FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_SI, FLD_SP, FLD_SA,
244: FLD_BYTES, FLD_STATE, FLD_PKTS, FLD_AGE, FLD_EXP, FLD_RULE, FLD_GW, NULL
245: };
246:
247: field_def *view8[] = {
1.37 mikeb 248: FLD_QUEUE, FLD_BANDW, FLD_SCHED, FLD_PKTS, FLD_BYTES,
1.1 canacar 249: FLD_DROPP, FLD_DROPB, FLD_QLEN, FLD_BORR, FLD_SUSP, FLD_PKTSPS,
250: FLD_BYTESPS, NULL
251: };
252:
253: /* Define orderings */
254: order_type order_list[] = {
255: {"none", "none", 'N', NULL},
256: {"bytes", "bytes", 'B', sort_size_callback},
257: {"expiry", "exp", 'E', sort_exp_callback},
258: {"packets", "pkt", 'P', sort_pkt_callback},
259: {"age", "age", 'A', sort_age_callback},
260: {"source addr", "src", 'F', sort_sa_callback},
261: {"dest. addr", "dest", 'T', sort_da_callback},
262: {"source port", "sport", 'S', sort_sp_callback},
263: {"dest. port", "dport", 'D', sort_dp_callback},
264: {"rate", "rate", 'R', sort_rate_callback},
265: {"peak", "peak", 'K', sort_peak_callback},
266: {NULL, NULL, 0, NULL}
267: };
268:
269: /* Define view managers */
270: struct view_manager state_mgr = {
271: "States", select_states, read_states, sort_states, print_header,
1.41 martijn 272: print_states, keyboard_callback, order_list, order_list
1.1 canacar 273: };
274:
275: struct view_manager rule_mgr = {
276: "Rules", select_rules, read_rules, NULL, print_header,
277: print_rules, keyboard_callback, NULL, NULL
278: };
279:
280: struct view_manager queue_mgr = {
281: "Queues", select_queues, read_queues, NULL, print_header,
282: print_queues, keyboard_callback, NULL, NULL
283: };
284:
285: field_view views[] = {
286: {view2, "states", '8', &state_mgr},
287: {view5, "rules", '9', &rule_mgr},
288: {view8, "queues", 'Q', &queue_mgr},
289: {NULL, NULL, 0, NULL}
290: };
291:
1.22 henning 292: /* queue structures from pfctl */
293:
294: struct queue_stats {
295: struct hfsc_class_stats data;
296: int valid;
297: struct timeval timestamp;
298: };
299:
300: struct pfctl_queue_node {
301: TAILQ_ENTRY(pfctl_queue_node) entries;
302: struct pf_queuespec qs;
303: struct queue_stats qstats;
304: struct queue_stats qstats_last;
305: int depth;
306: };
307: TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes);
1.1 canacar 308:
309: /* ordering functions */
310:
311: int
312: sort_size_callback(const void *s1, const void *s2)
313: {
1.43 bluhm 314: u_int64_t b1 = COUNTER(state_buf[* (size_t *) s1].bytes[0]) +
315: COUNTER(state_buf[* (size_t *) s1].bytes[1]);
316: u_int64_t b2 = COUNTER(state_buf[* (size_t *) s2].bytes[0]) +
317: COUNTER(state_buf[* (size_t *) s2].bytes[1]);
1.1 canacar 318: if (b2 > b1)
319: return sortdir;
320: if (b2 < b1)
321: return -sortdir;
322: return 0;
323: }
324:
325: int
326: sort_pkt_callback(const void *s1, const void *s2)
327: {
1.43 bluhm 328: u_int64_t p1 = COUNTER(state_buf[* (size_t *) s1].packets[0]) +
329: COUNTER(state_buf[* (size_t *) s1].packets[1]);
330: u_int64_t p2 = COUNTER(state_buf[* (size_t *) s2].packets[0]) +
331: COUNTER(state_buf[* (size_t *) s2].packets[1]);
1.1 canacar 332: if (p2 > p1)
333: return sortdir;
334: if (p2 < p1)
335: return -sortdir;
336: return 0;
337: }
338:
339: int
340: sort_age_callback(const void *s1, const void *s2)
341: {
1.43 bluhm 342: if (ntohl(state_buf[* (size_t *) s2].creation) >
343: ntohl(state_buf[* (size_t *) s1].creation))
1.1 canacar 344: return sortdir;
1.43 bluhm 345: if (ntohl(state_buf[* (size_t *) s2].creation) <
346: ntohl(state_buf[* (size_t *) s1].creation))
1.1 canacar 347: return -sortdir;
348: return 0;
349: }
350:
351: int
352: sort_exp_callback(const void *s1, const void *s2)
353: {
1.43 bluhm 354: if (ntohl(state_buf[* (size_t *) s2].expire) >
355: ntohl(state_buf[* (size_t *) s1].expire))
1.1 canacar 356: return sortdir;
1.43 bluhm 357: if (ntohl(state_buf[* (size_t *) s2].expire) <
358: ntohl(state_buf[* (size_t *) s1].expire))
1.1 canacar 359: return -sortdir;
360: return 0;
361: }
362:
363: int
364: sort_rate_callback(const void *s1, const void *s2)
365: {
366: struct sc_ent *e1 = state_cache[* (u_int32_t *) s1];
367: struct sc_ent *e2 = state_cache[* (u_int32_t *) s2];
368:
369: if (e1 == NULL)
370: return sortdir;
371: if (e2 == NULL)
372: return -sortdir;
1.26 sthen 373:
1.1 canacar 374: if (e2->rate > e1 -> rate)
375: return sortdir;
376: if (e2->rate < e1 -> rate)
377: return -sortdir;
378: return 0;
379: }
380:
381: int
382: sort_peak_callback(const void *s1, const void *s2)
383: {
384: struct sc_ent *e1 = state_cache[* (u_int32_t *) s1];
385: struct sc_ent *e2 = state_cache[* (u_int32_t *) s2];
386:
387: if (e2 == NULL)
388: return -sortdir;
389: if (e1 == NULL || e2 == NULL)
390: return 0;
1.26 sthen 391:
1.1 canacar 392: if (e2->peak > e1 -> peak)
393: return sortdir;
394: if (e2->peak < e1 -> peak)
395: return -sortdir;
396: return 0;
397: }
398:
399: int
400: compare_addr(int af, const struct pf_addr *a, const struct pf_addr *b)
401: {
402: switch (af) {
403: case AF_INET:
404: if (ntohl(a->addr32[0]) > ntohl(b->addr32[0]))
405: return 1;
406: if (a->addr32[0] != b->addr32[0])
407: return -1;
408: break;
409: case AF_INET6:
410: if (ntohl(a->addr32[0]) > ntohl(b->addr32[0]))
411: return 1;
412: if (a->addr32[0] != b->addr32[0])
413: return -1;
414: if (ntohl(a->addr32[1]) > ntohl(b->addr32[1]))
415: return 1;
416: if (a->addr32[1] != b->addr32[1])
417: return -1;
418: if (ntohl(a->addr32[2]) > ntohl(b->addr32[2]))
419: return 1;
420: if (a->addr32[2] != b->addr32[2])
421: return -1;
422: if (ntohl(a->addr32[3]) > ntohl(b->addr32[3]))
423: return 1;
424: if (a->addr32[3] != b->addr32[3])
425: return -1;
426: break;
427: }
1.26 sthen 428:
1.1 canacar 429: return 0;
430: }
431:
1.13 jsg 432: static __inline int
1.4 canacar 433: sort_addr_callback(const struct pfsync_state *s1,
434: const struct pfsync_state *s2, int dir)
1.1 canacar 435: {
436: const struct pf_addr *aa, *ab;
437: u_int16_t pa, pb;
1.20 claudio 438: int af, side, ret, ii, io;
1.1 canacar 439:
1.20 claudio 440: side = s1->direction == PF_IN ? PF_SK_STACK : PF_SK_WIRE;
1.1 canacar 441:
1.20 claudio 442: if (s1->key[side].af > s2->key[side].af)
1.1 canacar 443: return sortdir;
1.20 claudio 444: if (s1->key[side].af < s2->key[side].af)
1.1 canacar 445: return -sortdir;
1.20 claudio 446:
1.26 sthen 447: ii = io = 0;
1.1 canacar 448:
449: if (dir == PF_OUT) /* looking for source addr */
450: io = 1;
451: else /* looking for dest addr */
452: ii = 1;
1.20 claudio 453:
454: if (s1->key[PF_SK_STACK].af != s1->key[PF_SK_WIRE].af) {
455: dir = PF_OUT;
456: side = PF_SK_STACK;
457: } else {
458: dir = s1->direction;
459: side = PF_SK_WIRE;
460: }
461:
462: if (dir == PF_IN) {
1.1 canacar 463: aa = &s1->key[PF_SK_STACK].addr[ii];
464: pa = s1->key[PF_SK_STACK].port[ii];
1.20 claudio 465: af = s1->key[PF_SK_STACK].af;
466: } else {
467: aa = &s1->key[side].addr[io];
468: pa = s1->key[side].port[io];
469: af = s1->key[side].af;
470: }
471:
472: if (s2->key[PF_SK_STACK].af != s2->key[PF_SK_WIRE].af) {
473: dir = PF_OUT;
474: side = PF_SK_STACK;
1.1 canacar 475: } else {
1.20 claudio 476: dir = s2->direction;
477: side = PF_SK_WIRE;
1.1 canacar 478: }
479:
1.20 claudio 480: if (dir == PF_IN) {
1.16 deraadt 481: ab = &s2->key[PF_SK_STACK].addr[ii];
1.1 canacar 482: pb = s2->key[PF_SK_STACK].port[ii];
1.20 claudio 483: af = s1->key[PF_SK_STACK].af;
1.1 canacar 484: } else {
1.20 claudio 485: ab = &s2->key[side].addr[io];
486: pb = s2->key[side].port[io];
487: af = s1->key[side].af;
1.1 canacar 488: }
489:
490: ret = compare_addr(af, aa, ab);
491: if (ret)
492: return ret * sortdir;
493:
494: if (ntohs(pa) > ntohs(pb))
495: return sortdir;
496: return -sortdir;
497: }
498:
1.13 jsg 499: static __inline int
1.4 canacar 500: sort_port_callback(const struct pfsync_state *s1,
501: const struct pfsync_state *s2, int dir)
1.1 canacar 502: {
503: const struct pf_addr *aa, *ab;
504: u_int16_t pa, pb;
1.20 claudio 505: int af, side, ret, ii, io;
1.1 canacar 506:
1.20 claudio 507: side = s1->direction == PF_IN ? PF_SK_STACK : PF_SK_WIRE;
1.1 canacar 508:
1.20 claudio 509: if (s1->key[side].af > s2->key[side].af)
1.1 canacar 510: return sortdir;
1.20 claudio 511: if (s1->key[side].af < s2->key[side].af)
1.1 canacar 512: return -sortdir;
1.20 claudio 513:
1.26 sthen 514: ii = io = 0;
1.1 canacar 515:
516: if (dir == PF_OUT) /* looking for source addr */
517: io = 1;
518: else /* looking for dest addr */
519: ii = 1;
1.20 claudio 520:
521: if (s1->key[PF_SK_STACK].af != s1->key[PF_SK_WIRE].af) {
522: dir = PF_OUT;
523: side = PF_SK_STACK;
524: } else {
525: dir = s1->direction;
526: side = PF_SK_WIRE;
527: }
528:
529: if (dir == PF_IN) {
1.1 canacar 530: aa = &s1->key[PF_SK_STACK].addr[ii];
531: pa = s1->key[PF_SK_STACK].port[ii];
1.20 claudio 532: af = s1->key[PF_SK_STACK].af;
1.1 canacar 533: } else {
1.20 claudio 534: aa = &s1->key[side].addr[io];
535: pa = s1->key[side].port[io];
536: af = s1->key[side].af;
1.1 canacar 537: }
538:
1.20 claudio 539: if (s2->key[PF_SK_STACK].af != s2->key[PF_SK_WIRE].af) {
540: dir = PF_OUT;
541: side = PF_SK_STACK;
542: } else {
543: dir = s2->direction;
544: side = PF_SK_WIRE;
545: }
546:
547: if (dir == PF_IN) {
1.16 deraadt 548: ab = &s2->key[PF_SK_STACK].addr[ii];
1.1 canacar 549: pb = s2->key[PF_SK_STACK].port[ii];
1.20 claudio 550: af = s1->key[PF_SK_STACK].af;
1.1 canacar 551: } else {
1.20 claudio 552: ab = &s2->key[side].addr[io];
553: pb = s2->key[side].port[io];
554: af = s1->key[side].af;
1.1 canacar 555: }
556:
557:
558: if (ntohs(pa) > ntohs(pb))
559: return sortdir;
560: if (ntohs(pa) < ntohs(pb))
561: return - sortdir;
562:
563: ret = compare_addr(af, aa, ab);
564: if (ret)
565: return ret * sortdir;
566: return -sortdir;
567: }
568:
569: int
1.4 canacar 570: sort_sa_callback(const void *p1, const void *p2)
1.1 canacar 571: {
1.43 bluhm 572: struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
573: struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
1.4 canacar 574: return sort_addr_callback(s1, s2, PF_OUT);
1.1 canacar 575: }
576:
577: int
1.4 canacar 578: sort_da_callback(const void *p1, const void *p2)
1.1 canacar 579: {
1.43 bluhm 580: struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
581: struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
1.1 canacar 582: return sort_addr_callback(s1, s2, PF_IN);
583: }
584:
585: int
586: sort_sp_callback(const void *p1, const void *p2)
587: {
1.43 bluhm 588: struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
589: struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
1.1 canacar 590: return sort_port_callback(s1, s2, PF_OUT);
591: }
592:
593: int
594: sort_dp_callback(const void *p1, const void *p2)
595: {
1.43 bluhm 596: struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
597: struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
1.1 canacar 598: return sort_port_callback(s1, s2, PF_IN);
599: }
600:
601: void
602: sort_states(void)
603: {
604: order_type *ordering;
605:
606: if (curr_mgr == NULL)
607: return;
608:
609: ordering = curr_mgr->order_curr;
610:
611: if (ordering == NULL)
612: return;
613: if (ordering->func == NULL)
614: return;
615: if (state_buf == NULL)
616: return;
617: if (num_states <= 0)
618: return;
619:
1.43 bluhm 620: mergesort(state_ord, num_states, sizeof(size_t), ordering->func);
1.1 canacar 621: }
622:
623: /* state management functions */
624:
625: void
1.43 bluhm 626: alloc_buf(size_t ns)
1.1 canacar 627: {
1.43 bluhm 628: size_t len;
1.1 canacar 629:
630: if (ns < MIN_NUM_STATES)
631: ns = MIN_NUM_STATES;
632:
633: len = ns;
634:
635: if (len >= state_buf_len) {
636: len += NUM_STATE_INC;
1.29 doug 637: state_buf = reallocarray(state_buf, len,
638: sizeof(struct pfsync_state));
1.43 bluhm 639: state_ord = reallocarray(state_ord, len, sizeof(size_t));
1.29 doug 640: state_cache = reallocarray(state_cache, len,
641: sizeof(struct sc_ent *));
1.1 canacar 642: if (state_buf == NULL || state_ord == NULL ||
643: state_cache == NULL)
644: err(1, "realloc");
645: state_buf_len = len;
646: }
647: }
648:
649: int
650: select_states(void)
651: {
652: num_disp = num_states;
653: return (0);
654: }
655:
656: int
657: read_states(void)
658: {
659: struct pfioc_states ps;
1.43 bluhm 660: size_t n;
1.1 canacar 661:
662: if (pf_dev == -1)
663: return -1;
664:
665: for (;;) {
1.43 bluhm 666: size_t sbytes = state_buf_len * sizeof(struct pfsync_state);
1.1 canacar 667:
668: ps.ps_len = sbytes;
1.43 bluhm 669: ps.ps_states = state_buf;
1.1 canacar 670:
1.44 deraadt 671: if (ioctl(pf_dev, DIOCGETSTATES, &ps) == -1) {
1.1 canacar 672: error("DIOCGETSTATES");
673: }
1.4 canacar 674: num_states_all = ps.ps_len / sizeof(struct pfsync_state);
1.1 canacar 675:
676: if (ps.ps_len < sbytes)
677: break;
678:
679: alloc_buf(num_states_all);
680: }
681:
1.43 bluhm 682: num_states = num_states_all;
683: for (n = 0; n < num_states_all; n++)
1.1 canacar 684: state_ord[n] = n;
685:
686: if (cachestates) {
687: for (n = 0; n < num_states; n++)
688: state_cache[n] = cache_state(state_buf + n);
689: cache_endupdate();
690: }
691:
692: num_disp = num_states;
693: return 0;
694: }
695:
696: int
1.42 kn 697: unmask(struct pf_addr * m)
1.1 canacar 698: {
1.42 kn 699: int i = 31, j = 0, b = 0;
1.1 canacar 700: u_int32_t tmp;
701:
1.42 kn 702: while (j < 4 && m->addr32[j] == 0xffffffff) {
1.1 canacar 703: b += 32;
704: j++;
705: }
1.42 kn 706: if (j < 4) {
1.1 canacar 707: tmp = ntohl(m->addr32[j]);
708: for (i = 31; tmp & (1 << i); --i)
709: b++;
710: }
711: return (b);
712: }
713:
714: /* display functions */
715:
716: void
717: tb_print_addr(struct pf_addr * addr, struct pf_addr * mask, int af)
718: {
1.47 ! jsg 719: switch (af) {
! 720: case AF_INET:
! 721: tbprintf("%s", inetname(addr->v4));
! 722: break;
! 723: case AF_INET6:
! 724: tbprintf("%s", inet6name(&addr->v6));
! 725: break;
1.15 giovanni 726: }
1.1 canacar 727:
728: if (mask != NULL) {
729: if (!PF_AZERO(mask, af))
1.42 kn 730: tbprintf("/%u", unmask(mask));
1.1 canacar 731: }
732: }
1.4 canacar 733:
1.1 canacar 734: void
735: print_fld_host2(field_def *fld, struct pfsync_state_key *ks,
1.20 claudio 736: struct pfsync_state_key *kn, int idx)
1.1 canacar 737: {
738: struct pf_addr *as = &ks->addr[idx];
739: struct pf_addr *an = &kn->addr[idx];
740:
741: u_int16_t ps = ntohs(ks->port[idx]);
742: u_int16_t pn = ntohs(kn->port[idx]);
743:
1.20 claudio 744: int asf = ks->af;
745: int anf = kn->af;
746:
1.1 canacar 747: if (fld == NULL)
748: return;
749:
750: if (fld->width < 3) {
751: print_fld_str(fld, "*");
752: return;
753: }
754:
755: tb_start();
1.20 claudio 756: tb_print_addr(as, NULL, asf);
1.1 canacar 757:
1.20 claudio 758: if (asf == AF_INET)
1.1 canacar 759: tbprintf(":%u", ps);
760: else
761: tbprintf("[%u]", ps);
762:
763: print_fld_tb(fld);
764:
1.20 claudio 765: if (asf != anf || PF_ANEQ(as, an, asf) || ps != pn) {
1.1 canacar 766: tb_start();
1.20 claudio 767: tb_print_addr(an, NULL, anf);
1.1 canacar 768:
1.20 claudio 769: if (anf == AF_INET)
1.1 canacar 770: tbprintf(":%u", pn);
771: else
772: tbprintf("[%u]", pn);
773: print_fld_tb(FLD_GW);
774: }
775:
776: }
777:
778: void
779: print_fld_state(field_def *fld, unsigned int proto,
780: unsigned int s1, unsigned int s2)
781: {
782: int len;
1.26 sthen 783:
1.1 canacar 784: if (fld == NULL)
785: return;
786:
787: len = fld->width;
788: if (len < 1)
789: return;
1.26 sthen 790:
1.1 canacar 791: tb_start();
792:
793: if (proto == IPPROTO_TCP) {
794: if (s1 <= TCPS_TIME_WAIT && s2 <= TCPS_TIME_WAIT)
795: tbprintf("%s:%s", tcpstates[s1], tcpstates[s2]);
796: #ifdef PF_TCPS_PROXY_SRC
797: else if (s1 == PF_TCPS_PROXY_SRC ||
798: s2 == PF_TCPS_PROXY_SRC)
799: tbprintf("PROXY:SRC\n");
800: else if (s1 == PF_TCPS_PROXY_DST ||
801: s2 == PF_TCPS_PROXY_DST)
802: tbprintf("PROXY:DST\n");
803: #endif
804: else
805: tbprintf("<BAD STATE LEVELS>");
806: } else if (proto == IPPROTO_UDP && s1 < PFUDPS_NSTATES &&
807: s2 < PFUDPS_NSTATES) {
808: const char *states[] = PFUDPS_NAMES;
809: tbprintf("%s:%s", states[s1], states[s2]);
810: } else if (proto != IPPROTO_ICMP && s1 < PFOTHERS_NSTATES &&
811: s2 < PFOTHERS_NSTATES) {
812: /* XXX ICMP doesn't really have state levels */
813: const char *states[] = PFOTHERS_NAMES;
814: tbprintf("%s:%s", states[s1], states[s2]);
815: } else {
816: tbprintf("%u:%u", s1, s2);
817: }
818:
819: if (strlen(tmp_buf) > len) {
820: tb_start();
821: tbprintf("%u:%u", s1, s2);
822: }
823:
824: print_fld_tb(fld);
825: }
826:
827: int
1.4 canacar 828: print_state(struct pfsync_state * s, struct sc_ent * ent)
1.1 canacar 829: {
1.4 canacar 830: struct pfsync_state_peer *src, *dst;
1.1 canacar 831: struct protoent *p;
1.4 canacar 832: u_int64_t sz;
1.20 claudio 833: int afto, dir;
834:
835: afto = s->key[PF_SK_STACK].af == s->key[PF_SK_WIRE].af ? 0 : 1;
836: dir = afto ? PF_OUT : s->direction;
1.1 canacar 837:
1.20 claudio 838: if (dir == PF_OUT) {
1.1 canacar 839: src = &s->src;
840: dst = &s->dst;
841: } else {
842: src = &s->dst;
843: dst = &s->src;
844: }
845:
846: p = getprotobynumber(s->proto);
847:
848: if (p != NULL)
849: print_fld_str(FLD_PROTO, p->p_name);
850: else
851: print_fld_uint(FLD_PROTO, s->proto);
852:
1.20 claudio 853: if (dir == PF_OUT) {
854: print_fld_host2(FLD_SRC,
855: &s->key[afto ? PF_SK_STACK : PF_SK_WIRE],
856: &s->key[PF_SK_STACK], 1);
857: print_fld_host2(FLD_DEST,
858: &s->key[afto ? PF_SK_STACK : PF_SK_WIRE],
859: &s->key[afto ? PF_SK_WIRE : PF_SK_STACK], 0);
1.1 canacar 860: } else {
861: print_fld_host2(FLD_SRC, &s->key[PF_SK_STACK],
1.20 claudio 862: &s->key[PF_SK_WIRE], 0);
1.1 canacar 863: print_fld_host2(FLD_DEST, &s->key[PF_SK_STACK],
1.20 claudio 864: &s->key[PF_SK_WIRE], 1);
1.1 canacar 865: }
866:
1.20 claudio 867: if (dir == PF_OUT)
1.1 canacar 868: print_fld_str(FLD_DIR, "Out");
869: else
870: print_fld_str(FLD_DIR, "In");
871:
872: print_fld_state(FLD_STATE, s->proto, src->state, dst->state);
1.3 mcbride 873: print_fld_age(FLD_AGE, ntohl(s->creation));
874: print_fld_age(FLD_EXP, ntohl(s->expire));
1.4 canacar 875:
876: sz = COUNTER(s->bytes[0]) + COUNTER(s->bytes[1]);
877:
878: print_fld_size(FLD_PKTS, COUNTER(s->packets[0]) +
879: COUNTER(s->packets[1]));
880: print_fld_size(FLD_BYTES, sz);
1.3 mcbride 881: print_fld_rate(FLD_SA, (s->creation) ?
1.33 canacar 882: ((double)sz/(double)ntohl(s->creation)) : -1);
1.1 canacar 883:
1.9 canacar 884: print_fld_uint(FLD_RULE, ntohl(s->rule));
1.1 canacar 885: if (cachestates && ent != NULL) {
886: print_fld_rate(FLD_SI, ent->rate);
887: print_fld_rate(FLD_SP, ent->peak);
888: }
889:
890: end_line();
891: return 1;
892: }
893:
894: void
895: print_states(void)
896: {
897: int n, count = 0;
898:
899: for (n = dispstart; n < num_disp; n++) {
900: count += print_state(state_buf + state_ord[n],
901: state_cache[state_ord[n]]);
902: if (maxprint > 0 && count >= maxprint)
903: break;
904: }
905: }
906:
907: /* rule display */
908:
909: struct pf_rule *rules = NULL;
910: u_int32_t alloc_rules = 0;
911:
912: int
913: select_rules(void)
914: {
915: num_disp = num_rules;
916: return (0);
917: }
918:
919:
920: void
921: add_rule_alloc(u_int32_t nr)
922: {
923: if (nr == 0)
924: return;
925:
926: num_rules += nr;
927:
928: if (rules == NULL) {
1.29 doug 929: rules = reallocarray(NULL, num_rules, sizeof(struct pf_rule));
1.1 canacar 930: if (rules == NULL)
931: err(1, "malloc");
932: alloc_rules = num_rules;
933: } else if (num_rules > alloc_rules) {
1.29 doug 934: rules = reallocarray(rules, num_rules, sizeof(struct pf_rule));
1.1 canacar 935: if (rules == NULL)
936: err(1, "realloc");
937: alloc_rules = num_rules;
938: }
939: }
940:
941: int label_length;
942:
1.46 sashan 943: void
944: close_pf_trans(u_int32_t ticket)
945: {
946: if (ioctl(pf_dev, DIOCXEND, &ticket) == -1)
947: error("DIOCXEND: %s", strerror(errno));
948: }
949:
1.1 canacar 950: int
951: read_anchor_rules(char *anchor)
952: {
953: struct pfioc_rule pr;
954: u_int32_t nr, num, off;
1.4 canacar 955: int len;
1.1 canacar 956:
957: if (pf_dev < 0)
958: return (-1);
959:
960: memset(&pr, 0, sizeof(pr));
961: strlcpy(pr.anchor, anchor, sizeof(pr.anchor));
1.4 canacar 962:
1.44 deraadt 963: if (ioctl(pf_dev, DIOCGETRULES, &pr) == -1) {
1.1 canacar 964: error("anchor %s: %s", anchor, strerror(errno));
965: return (-1);
966: }
967:
968: off = num_rules;
969: num = pr.nr;
970: add_rule_alloc(num);
971:
972: for (nr = 0; nr < num; ++nr) {
973: pr.nr = nr;
1.44 deraadt 974: if (ioctl(pf_dev, DIOCGETRULE, &pr) == -1) {
1.1 canacar 975: error("DIOCGETRULE: %s", strerror(errno));
1.46 sashan 976: close_pf_trans(pr.ticket);
1.1 canacar 977: return (-1);
978: }
979: /* XXX overload pr.anchor, to store a pointer to
980: * anchor name */
981: pr.rule.anchor = (struct pf_anchor *) anchor;
1.4 canacar 982: len = strlen(pr.rule.label);
983: if (len > label_length)
984: label_length = len;
1.1 canacar 985: rules[off + nr] = pr.rule;
986: }
1.46 sashan 987:
988: close_pf_trans(pr.ticket);
1.1 canacar 989:
990: return (num);
991: }
992:
993: struct anchor_name {
1.30 deraadt 994: char name[PATH_MAX];
1.1 canacar 995: struct anchor_name *next;
996: u_int32_t ref;
997: };
998:
999: struct anchor_name *anchor_root = NULL;
1000: struct anchor_name *anchor_end = NULL;
1001: struct anchor_name *anchor_free = NULL;
1002:
1003: struct anchor_name*
1004: alloc_anchor_name(const char *path)
1005: {
1006: struct anchor_name *a;
1007:
1008: a = anchor_free;
1009: if (a == NULL) {
1.32 deraadt 1010: a = malloc(sizeof(struct anchor_name));
1.1 canacar 1011: if (a == NULL)
1012: return (NULL);
1013: } else
1014: anchor_free = a->next;
1015:
1016: if (anchor_root == NULL)
1017: anchor_end = a;
1018:
1019: a->next = anchor_root;
1020: anchor_root = a;
1021:
1022: a->ref = 0;
1023: strlcpy(a->name, path, sizeof(a->name));
1024: return (a);
1025: }
1026:
1027: void
1028: reset_anchor_names(void)
1029: {
1030: if (anchor_end == NULL)
1031: return;
1032:
1033: anchor_end->next = anchor_free;
1034: anchor_free = anchor_root;
1035: anchor_root = anchor_end = NULL;
1036: }
1037:
1038: struct pfioc_ruleset ruleset;
1039: char *rs_end = NULL;
1040:
1041: int
1042: read_rulesets(const char *path)
1043: {
1044: char *pre;
1045: struct anchor_name *a;
1046: u_int32_t nr, ns;
1047: int len;
1048:
1049: if (path == NULL)
1050: ruleset.path[0] = '\0';
1051: else if (strlcpy(ruleset.path, path, sizeof(ruleset.path)) >=
1052: sizeof(ruleset.path))
1053: return (-1);
1054:
1055: /* a persistent storage for anchor names */
1056: a = alloc_anchor_name(ruleset.path);
1057: if (a == NULL)
1058: return (-1);
1059:
1060: len = read_anchor_rules(a->name);
1061: if (len < 0)
1062: return (-1);
1063:
1064: a->ref += len;
1065:
1.44 deraadt 1066: if (ioctl(pf_dev, DIOCGETRULESETS, &ruleset) == -1) {
1.1 canacar 1067: error("DIOCGETRULESETS: %s", strerror(errno));
1068: return (-1);
1069: }
1070:
1071: ns = ruleset.nr;
1072:
1073: if (rs_end == NULL)
1074: rs_end = ruleset.path + sizeof(ruleset.path);
1075:
1076: /* 'pre' tracks the previous level on the anchor */
1077: pre = strchr(ruleset.path, 0);
1078: len = rs_end - pre;
1079: if (len < 1)
1080: return (-1);
1081: --len;
1082:
1083: for (nr = 0; nr < ns; ++nr) {
1084: ruleset.nr = nr;
1.44 deraadt 1085: if (ioctl(pf_dev, DIOCGETRULESET, &ruleset) == -1) {
1.1 canacar 1086: error("DIOCGETRULESET: %s", strerror(errno));
1087: return (-1);
1088: }
1089: *pre = '/';
1090: if (strlcpy(pre + 1, ruleset.name, len) < len)
1091: read_rulesets(ruleset.path);
1092: *pre = '\0';
1093: }
1094:
1095: return (0);
1096: }
1097:
1098: void
1099: compute_anchor_field(void)
1100: {
1101: struct anchor_name *a;
1102: int sum, cnt, mx, nx;
1103: sum = cnt = mx = 0;
1104:
1105: for (a = anchor_root; a != NULL; a = a->next, cnt++) {
1106: int len;
1107: if (a->ref == 0)
1108: continue;
1109: len = strlen(a->name);
1110: sum += len;
1111: if (len > mx)
1112: mx = len;
1113: }
1114:
1115: nx = sum/cnt;
1116: if (nx < ANCHOR_FLD_SIZE)
1117: nx = (mx < ANCHOR_FLD_SIZE) ? mx : ANCHOR_FLD_SIZE;
1118:
1119: if (FLD_ANCHOR->max_width != mx ||
1120: FLD_ANCHOR->norm_width != nx) {
1121: FLD_ANCHOR->max_width = mx;
1122: FLD_ANCHOR->norm_width = nx;
1123: field_setup();
1124: need_update = 1;
1125: }
1126: }
1127:
1128: int
1129: read_rules(void)
1130: {
1.4 canacar 1131: int ret, nw, mw;
1.1 canacar 1132: num_rules = 0;
1133:
1134: if (pf_dev == -1)
1135: return (-1);
1136:
1137: label_length = MIN_LABEL_SIZE;
1138:
1139: reset_anchor_names();
1140: ret = read_rulesets(NULL);
1141: compute_anchor_field();
1142:
1.4 canacar 1143: nw = mw = label_length;
1144: if (nw > 16)
1145: nw = 16;
1146:
1147: if (FLD_LABEL->norm_width != nw ||
1148: FLD_LABEL->max_width != mw) {
1149: FLD_LABEL->norm_width = nw;
1150: FLD_LABEL->max_width = mw;
1151: field_setup();
1152: need_update = 1;
1.1 canacar 1153: }
1154:
1155: num_disp = num_rules;
1156: return (ret);
1157: }
1158:
1159: void
1160: tb_print_addrw(struct pf_addr_wrap *addr, struct pf_addr *mask, u_int8_t af)
1161: {
1162: switch (addr->type) {
1163: case PF_ADDR_ADDRMASK:
1164: tb_print_addr(&addr->v.a.addr, mask, af);
1165: break;
1166: case PF_ADDR_NOROUTE:
1167: tbprintf("noroute");
1168: break;
1169: case PF_ADDR_DYNIFTL:
1170: tbprintf("(%s)", addr->v.ifname);
1171: break;
1172: case PF_ADDR_TABLE:
1173: tbprintf("<%s>", addr->v.tblname);
1174: break;
1175: default:
1176: tbprintf("UNKNOWN");
1177: break;
1178: }
1179: }
1180:
1181: void
1182: tb_print_op(u_int8_t op, const char *a1, const char *a2)
1183: {
1184: if (op == PF_OP_IRG)
1185: tbprintf("%s >< %s ", a1, a2);
1186: else if (op == PF_OP_XRG)
1187: tbprintf("%s <> %s ", a1, a2);
1188: else if (op == PF_OP_RRG)
1189: tbprintf("%s:%s ", a1, a2);
1190: else if (op == PF_OP_EQ)
1191: tbprintf("= %s ", a1);
1192: else if (op == PF_OP_NE)
1193: tbprintf("!= %s ", a1);
1194: else if (op == PF_OP_LT)
1195: tbprintf("< %s ", a1);
1196: else if (op == PF_OP_LE)
1197: tbprintf("<= %s ", a1);
1198: else if (op == PF_OP_GT)
1199: tbprintf("> %s ", a1);
1200: else if (op == PF_OP_GE)
1201: tbprintf(">= %s ", a1);
1202: }
1203:
1204: void
1205: tb_print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, char *proto)
1206: {
1207: char a1[6], a2[6];
1208: struct servent *s = getservbyport(p1, proto);
1209:
1210: p1 = ntohs(p1);
1211: p2 = ntohs(p2);
1212: snprintf(a1, sizeof(a1), "%u", p1);
1213: snprintf(a2, sizeof(a2), "%u", p2);
1214: tbprintf("port ");
1215: if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE))
1216: tb_print_op(op, s->s_name, a2);
1217: else
1218: tb_print_op(op, a1, a2);
1219: }
1220:
1221: void
1222: tb_print_fromto(struct pf_rule_addr *src, struct pf_rule_addr *dst,
1223: u_int8_t af, u_int8_t proto)
1224: {
1225: if (
1226: PF_AZERO(PT_ADDR(src), AF_INET6) &&
1227: PF_AZERO(PT_ADDR(dst), AF_INET6) &&
1228: ! PT_NOROUTE(src) && ! PT_NOROUTE(dst) &&
1229: PF_AZERO(PT_MASK(src), AF_INET6) &&
1230: PF_AZERO(PT_MASK(dst), AF_INET6) &&
1231: !src->port_op && !dst->port_op)
1232: tbprintf("all ");
1233: else {
1234: tbprintf("from ");
1235: if (PT_NOROUTE(src))
1236: tbprintf("no-route ");
1237: else if (PF_AZERO(PT_ADDR(src), AF_INET6) &&
1238: PF_AZERO(PT_MASK(src), AF_INET6))
1239: tbprintf("any ");
1240: else {
1241: if (src->neg)
1242: tbprintf("! ");
1243: tb_print_addrw(&src->addr, PT_MASK(src), af);
1244: tbprintf(" ");
1245: }
1246: if (src->port_op)
1247: tb_print_port(src->port_op, src->port[0],
1248: src->port[1],
1249: proto == IPPROTO_TCP ? "tcp" : "udp");
1.26 sthen 1250:
1.1 canacar 1251: tbprintf("to ");
1252: if (PT_NOROUTE(dst))
1253: tbprintf("no-route ");
1254: else if (PF_AZERO(PT_ADDR(dst), AF_INET6) &&
1255: PF_AZERO(PT_MASK(dst), AF_INET6))
1256: tbprintf("any ");
1257: else {
1258: if (dst->neg)
1259: tbprintf("! ");
1260: tb_print_addrw(&dst->addr, PT_MASK(dst), af);
1261: tbprintf(" ");
1262: }
1263: if (dst->port_op)
1264: tb_print_port(dst->port_op, dst->port[0],
1265: dst->port[1],
1266: proto == IPPROTO_TCP ? "tcp" : "udp");
1267: }
1268: }
1269:
1270: void
1.45 millert 1271: tb_print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t)
1.1 canacar 1272: {
1273: char a1[11], a2[11];
1274:
1.45 millert 1275: snprintf(a1, sizeof(a1), "%u", i1);
1276: snprintf(a2, sizeof(a2), "%u", i2);
1.1 canacar 1277:
1278: tbprintf("%s ", t);
1.45 millert 1279: if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE))
1.1 canacar 1280: tb_print_op(op, "unknown", a2);
1281: else
1282: tb_print_op(op, a1, a2);
1283: }
1284:
1285: void
1286: tb_print_flags(u_int8_t f)
1287: {
1288: const char *tcpflags = "FSRPAUEW";
1289: int i;
1290:
1291: for (i = 0; tcpflags[i]; ++i)
1292: if (f & (1 << i))
1293: tbprintf("%c", tcpflags[i]);
1294: }
1295:
1296: void
1297: print_rule(struct pf_rule *pr)
1298: {
1.11 henning 1299: static const char *actiontypes[] = { "Pass", "Block", "Scrub",
1300: "no Scrub", "Nat", "no Nat", "Binat", "no Binat", "Rdr",
1301: "no Rdr", "SynProxy Block", "Defer", "Match" };
1.1 canacar 1302: int numact = sizeof(actiontypes) / sizeof(char *);
1303:
1304: static const char *routetypes[] = { "", "fastroute", "route-to",
1305: "dup-to", "reply-to" };
1306:
1307: int numroute = sizeof(routetypes) / sizeof(char *);
1308:
1309: if (pr == NULL) return;
1310:
1311: print_fld_str(FLD_LABEL, pr->label);
1312: print_fld_size(FLD_STATS, pr->states_tot);
1313:
1314: print_fld_size(FLD_PKTS, pr->packets[0] + pr->packets[1]);
1315: print_fld_size(FLD_BYTES, pr->bytes[0] + pr->bytes[1]);
1.4 canacar 1316:
1.1 canacar 1317: print_fld_uint(FLD_RULE, pr->nr);
1.5 sthen 1318: if (pr->direction == PF_OUT)
1319: print_fld_str(FLD_DIR, "Out");
1320: else if (pr->direction == PF_IN)
1321: print_fld_str(FLD_DIR, "In");
1322: else
1323: print_fld_str(FLD_DIR, "Any");
1324:
1.1 canacar 1325: if (pr->quick)
1326: print_fld_str(FLD_QUICK, "Quick");
1327:
1328: if (pr->keep_state == PF_STATE_NORMAL)
1329: print_fld_str(FLD_KST, "Keep");
1330: else if (pr->keep_state == PF_STATE_MODULATE)
1331: print_fld_str(FLD_KST, "Mod");
1.31 jsg 1332: else if (pr->keep_state == PF_STATE_SYNPROXY)
1.1 canacar 1333: print_fld_str(FLD_KST, "Syn");
1334: if (pr->log == 1)
1335: print_fld_str(FLD_LOG, "Log");
1336: else if (pr->log == 2)
1337: print_fld_str(FLD_LOG, "All");
1338:
1.12 canacar 1339: if (pr->action >= numact)
1.1 canacar 1340: print_fld_uint(FLD_ACTION, pr->action);
1341: else print_fld_str(FLD_ACTION, actiontypes[pr->action]);
1.12 canacar 1342:
1.1 canacar 1343: if (pr->proto) {
1344: struct protoent *p = getprotobynumber(pr->proto);
1345:
1346: if (p != NULL)
1347: print_fld_str(FLD_PROTO, p->p_name);
1348: else
1349: print_fld_uint(FLD_PROTO, pr->proto);
1350: }
1351:
1352: if (pr->ifname[0]) {
1353: tb_start();
1354: if (pr->ifnot)
1355: tbprintf("!");
1356: tbprintf("%s", pr->ifname);
1357: print_fld_tb(FLD_IF);
1358: }
1359: if (pr->max_states)
1360: print_fld_uint(FLD_STMAX, pr->max_states);
1.4 canacar 1361:
1.1 canacar 1362: /* print info field */
1363:
1364: tb_start();
1.4 canacar 1365:
1.1 canacar 1366: if (pr->action == PF_DROP) {
1367: if (pr->rule_flag & PFRULE_RETURNRST)
1368: tbprintf("return-rst ");
1369: #ifdef PFRULE_RETURN
1370: else if (pr->rule_flag & PFRULE_RETURN)
1371: tbprintf("return ");
1372: #endif
1373: #ifdef PFRULE_RETURNICMP
1374: else if (pr->rule_flag & PFRULE_RETURNICMP)
1375: tbprintf("return-icmp ");
1376: #endif
1377: else
1378: tbprintf("drop ");
1379: }
1380:
1381: if (pr->rt > 0 && pr->rt < numroute) {
1382: tbprintf("%s ", routetypes[pr->rt]);
1383: }
1.4 canacar 1384:
1.1 canacar 1385: if (pr->af) {
1386: if (pr->af == AF_INET)
1387: tbprintf("inet ");
1388: else
1389: tbprintf("inet6 ");
1390: }
1391:
1392: tb_print_fromto(&pr->src, &pr->dst, pr->af, pr->proto);
1.4 canacar 1393:
1.1 canacar 1394: if (pr->uid.op)
1395: tb_print_ugid(pr->uid.op, pr->uid.uid[0], pr->uid.uid[1],
1.45 millert 1396: "user");
1.1 canacar 1397: if (pr->gid.op)
1398: tb_print_ugid(pr->gid.op, pr->gid.gid[0], pr->gid.gid[1],
1.45 millert 1399: "group");
1.1 canacar 1400:
1.8 mcbride 1401: if (pr->action == PF_PASS &&
1402: (pr->proto == 0 || pr->proto == IPPROTO_TCP) &&
1403: (pr->flags != TH_SYN || pr->flagset != (TH_SYN | TH_ACK) )) {
1404: tbprintf("flags ");
1405: if (pr->flags || pr->flagset) {
1406: tb_print_flags(pr->flags);
1407: tbprintf("/");
1408: tb_print_flags(pr->flagset);
1409: } else
1410: tbprintf("any ");
1.1 canacar 1411: }
1412:
1413: tbprintf(" ");
1414:
1415: if (pr->tos)
1416: tbprintf("tos 0x%2.2x ", pr->tos);
1417: #ifdef PFRULE_FRAGMENT
1418: if (pr->rule_flag & PFRULE_FRAGMENT)
1419: tbprintf("fragment ");
1420: #endif
1421: #ifdef PFRULE_NODF
1422: if (pr->rule_flag & PFRULE_NODF)
1423: tbprintf("no-df ");
1424: #endif
1425: #ifdef PFRULE_RANDOMID
1426: if (pr->rule_flag & PFRULE_RANDOMID)
1427: tbprintf("random-id ");
1428: #endif
1429: if (pr->min_ttl)
1430: tbprintf("min-ttl %d ", pr->min_ttl);
1431: if (pr->max_mss)
1432: tbprintf("max-mss %d ", pr->max_mss);
1433: if (pr->allow_opts)
1434: tbprintf("allow-opts ");
1435:
1.10 henning 1436: /* XXX more missing */
1.1 canacar 1437:
1438: if (pr->qname[0] && pr->pqname[0])
1439: tbprintf("queue(%s, %s) ", pr->qname, pr->pqname);
1440: else if (pr->qname[0])
1441: tbprintf("queue %s ", pr->qname);
1.4 canacar 1442:
1.1 canacar 1443: if (pr->tagname[0])
1444: tbprintf("tag %s ", pr->tagname);
1445: if (pr->match_tagname[0]) {
1446: if (pr->match_tag_not)
1447: tbprintf("! ");
1448: tbprintf("tagged %s ", pr->match_tagname);
1449: }
1.4 canacar 1450:
1.1 canacar 1451: print_fld_tb(FLD_RINFO);
1452:
1453: /* XXX anchor field overloaded with anchor name */
1454: print_fld_str(FLD_ANCHOR, (char *)pr->anchor);
1455: tb_end();
1456:
1457: end_line();
1458: }
1459:
1460: void
1461: print_rules(void)
1462: {
1463: u_int32_t n, count = 0;
1.26 sthen 1464:
1.1 canacar 1465: for (n = dispstart; n < num_rules; n++) {
1466: print_rule(rules + n);
1467: count ++;
1468: if (maxprint > 0 && count >= maxprint)
1469: break;
1470: }
1471: }
1472:
1473: /* queue display */
1.22 henning 1474: struct pfctl_queue_node *
1475: pfctl_find_queue_node(const char *qname, const char *ifname)
1476: {
1477: struct pfctl_queue_node *node;
1478:
1479: TAILQ_FOREACH(node, &qnodes, entries)
1480: if (!strcmp(node->qs.qname, qname)
1481: && !(strcmp(node->qs.ifname, ifname)))
1482: return (node);
1483: return (NULL);
1484: }
1485:
1486: void
1487: pfctl_insert_queue_node(const struct pf_queuespec qs,
1488: const struct queue_stats qstats)
1489: {
1490: struct pfctl_queue_node *node, *parent;
1491:
1492: node = calloc(1, sizeof(struct pfctl_queue_node));
1493: if (node == NULL)
1494: err(1, "pfctl_insert_queue_node: calloc");
1495: memcpy(&node->qs, &qs, sizeof(qs));
1496: memcpy(&node->qstats, &qstats, sizeof(qstats));
1497:
1498: if (node->qs.parent[0]) {
1499: parent = pfctl_find_queue_node(node->qs.parent,
1500: node->qs.ifname);
1501: if (parent)
1502: node->depth = parent->depth + 1;
1503: }
1504:
1505: TAILQ_INSERT_TAIL(&qnodes, node, entries);
1506: }
1507:
1508: int
1509: pfctl_update_qstats(void)
1510: {
1511: struct pfctl_queue_node *node;
1512: struct pfioc_queue pq;
1513: struct pfioc_qstats pqs;
1514: u_int32_t mnr, nr;
1515: struct queue_stats qstats;
1516: static u_int32_t last_ticket;
1517:
1518: memset(&pq, 0, sizeof(pq));
1519: memset(&pqs, 0, sizeof(pqs));
1520: memset(&qstats, 0, sizeof(qstats));
1521:
1522: if (pf_dev < 0)
1523: return (-1);
1524:
1.44 deraadt 1525: if (ioctl(pf_dev, DIOCGETQUEUES, &pq) == -1) {
1.22 henning 1526: error("DIOCGETQUEUES: %s", strerror(errno));
1527: return (-1);
1528: }
1529:
1530: /* if a new set is found, start over */
1531: if (pq.ticket != last_ticket)
1.23 pelikan 1532: while ((node = TAILQ_FIRST(&qnodes)) != NULL) {
1.22 henning 1533: TAILQ_REMOVE(&qnodes, node, entries);
1.23 pelikan 1534: free(node);
1535: }
1.22 henning 1536: last_ticket = pq.ticket;
1537:
1538: num_queues = mnr = pq.nr;
1539: for (nr = 0; nr < mnr; ++nr) {
1540: pqs.nr = nr;
1541: pqs.ticket = pq.ticket;
1542: pqs.buf = &qstats.data;
1543: pqs.nbytes = sizeof(qstats.data);
1.44 deraadt 1544: if (ioctl(pf_dev, DIOCGETQSTATS, &pqs) == -1) {
1.22 henning 1545: error("DIOCGETQSTATS: %s", strerror(errno));
1546: return (-1);
1547: }
1.36 mikeb 1548: qstats.valid = 1;
1549: gettimeofday(&qstats.timestamp, NULL);
1550: if ((node = pfctl_find_queue_node(pqs.queue.qname,
1551: pqs.queue.ifname)) != NULL) {
1552: memcpy(&node->qstats_last, &node->qstats,
1553: sizeof(struct queue_stats));
1554: memcpy(&node->qstats, &qstats,
1555: sizeof(struct queue_stats));
1556: } else {
1557: pfctl_insert_queue_node(pqs.queue, qstats);
1558: }
1.22 henning 1559: }
1560: return (0);
1561: }
1562:
1.1 canacar 1563: int
1564: select_queues(void)
1565: {
1.24 henning 1566: num_disp = num_queues;
1.1 canacar 1567: return (0);
1568: }
1569:
1570: int
1571: read_queues(void)
1572: {
1.24 henning 1573: num_disp = num_queues = 0;
1.22 henning 1574:
1575: if (pfctl_update_qstats() < 0)
1.1 canacar 1576: return (-1);
1.24 henning 1577: num_disp = num_queues;
1.25 sthen 1578:
1.1 canacar 1579: return(0);
1580: }
1581:
1582: double
1583: calc_interval(struct timeval *cur_time, struct timeval *last_time)
1584: {
1585: double sec;
1586:
1587: sec = (double)(cur_time->tv_sec - last_time->tv_sec) +
1588: (double)(cur_time->tv_usec - last_time->tv_usec) / 1000000;
1589:
1590: return (sec);
1591: }
1592:
1593: double
1594: calc_rate(u_int64_t new_bytes, u_int64_t last_bytes, double interval)
1595: {
1596: double rate;
1597:
1598: rate = (double)(new_bytes - last_bytes) / interval;
1599: return (rate);
1600: }
1601:
1602: double
1603: calc_pps(u_int64_t new_pkts, u_int64_t last_pkts, double interval)
1604: {
1605: double pps;
1606:
1607: pps = (double)(new_pkts - last_pkts) / interval;
1608: return (pps);
1609: }
1610:
1611: void
1.22 henning 1612: print_queue_node(struct pfctl_queue_node *node)
1613: {
1.38 mikeb 1614: u_int rate, rtmp;
1.22 henning 1615: int i;
1616: double interval, pps, bps;
1617: static const char unit[] = " KMG";
1618:
1619: tb_start();
1620: for (i = 0; i < node->depth; i++)
1621: tbprintf(" ");
1622: tbprintf("%s", node->qs.qname);
1.28 sthen 1623: if (i == 0 && node->qs.ifname[0])
1624: tbprintf(" on %s ", node->qs.ifname);
1.22 henning 1625: print_fld_tb(FLD_QUEUE);
1626:
1627: // XXX: missing min, max, burst
1628: tb_start();
1.40 mikeb 1629: rate = node->qs.linkshare.m2.absolute;
1630: for (i = 0; rate > 9999 && i <= 3; i++) {
1631: rtmp = rate / 1000;
1632: if (rtmp <= 9999)
1633: rtmp += (rate % 1000) / 500;
1634: rate = rtmp;
1635: }
1636: if (rate == 0 && (node->qs.flags & PFQS_FLOWQUEUE)) {
1.39 mikeb 1637: /*
1638: * XXX We're abusing the fact that 'flows' in
1639: * the fqcodel_stats structure is at the same
1640: * spot as the 'period' in hfsc_class_stats.
1641: */
1642: tbprintf("%u", node->qstats.data.period);
1.40 mikeb 1643: } else
1.39 mikeb 1644: tbprintf("%u%c", rate, unit[i]);
1.22 henning 1645: print_fld_tb(FLD_BANDW);
1.39 mikeb 1646:
1647: print_fld_str(FLD_SCHED, node->qs.flags & PFQS_FLOWQUEUE ?
1648: "flow" : "fifo");
1.22 henning 1649:
1650: if (node->qstats.valid && node->qstats_last.valid)
1651: interval = calc_interval(&node->qstats.timestamp,
1652: &node->qstats_last.timestamp);
1653: else
1654: interval = 0;
1655:
1656: print_fld_size(FLD_PKTS, node->qstats.data.xmit_cnt.packets);
1657: print_fld_size(FLD_BYTES, node->qstats.data.xmit_cnt.bytes);
1658: print_fld_size(FLD_DROPP, node->qstats.data.drop_cnt.packets);
1659: print_fld_size(FLD_DROPB, node->qstats.data.drop_cnt.bytes);
1660: print_fld_size(FLD_QLEN, node->qstats.data.qlength);
1661:
1662: if (interval > 0) {
1663: pps = calc_pps(node->qstats.data.xmit_cnt.packets,
1664: node->qstats_last.data.xmit_cnt.packets, interval);
1665: bps = calc_rate(node->qstats.data.xmit_cnt.bytes,
1666: node->qstats_last.data.xmit_cnt.bytes, interval);
1667:
1668: tb_start();
1669: if (pps > 0 && pps < 1)
1670: tbprintf("%-3.1lf", pps);
1671: else
1672: tbprintf("%u", (unsigned int)pps);
1673:
1674: print_fld_tb(FLD_PKTSPS);
1675: print_fld_bw(FLD_BYTESPS, bps);
1676: }
1677: }
1678:
1679: void
1.1 canacar 1680: print_queues(void)
1681: {
1.22 henning 1682: uint32_t n, count, start;
1683: struct pfctl_queue_node *node;
1684:
1685: n = count = 0;
1686: start = dispstart;
1687:
1688: TAILQ_FOREACH(node, &qnodes, entries) {
1689: if (n < start) {
1690: n++;
1691: continue;
1692: }
1693: print_queue_node(node);
1694: end_line();
1695: count++;
1696: if (maxprint > 0 && count >= maxprint)
1697: return;
1.1 canacar 1698: }
1699: }
1700:
1701: /* main program functions */
1702:
1703: void
1.7 canacar 1704: update_cache(void)
1.1 canacar 1705: {
1706: static int pstate = -1;
1707: if (pstate == cachestates)
1708: return;
1709:
1710: pstate = cachestates;
1711: if (cachestates) {
1712: show_field(FLD_SI);
1713: show_field(FLD_SP);
1714: gotsig_alarm = 1;
1715: } else {
1716: hide_field(FLD_SI);
1717: hide_field(FLD_SP);
1718: need_update = 1;
1719: }
1720: field_setup();
1721: }
1722:
1.7 canacar 1723: int
1.1 canacar 1724: initpftop(void)
1725: {
1726: struct pf_status status;
1727: field_view *v;
1728: int cachesize = DEFAULT_CACHE_SIZE;
1729:
1730: v = views;
1731: while(v->name != NULL)
1732: add_view(v++);
1733:
1734: pf_dev = open("/dev/pf", O_RDONLY);
1735: if (pf_dev == -1) {
1736: alloc_buf(0);
1.44 deraadt 1737: } else if (ioctl(pf_dev, DIOCGETSTATUS, &status) == -1) {
1.1 canacar 1738: warn("DIOCGETSTATUS");
1739: alloc_buf(0);
1740: } else
1741: alloc_buf(status.states);
1742:
1743: /* initialize cache with given size */
1744: if (cache_init(cachesize))
1745: warnx("Failed to initialize cache.");
1746: else if (interactive && cachesize > 0)
1747: cachestates = 1;
1748:
1749: update_cache();
1750:
1751: show_field(FLD_STMAX);
1752: show_field(FLD_ANCHOR);
1.7 canacar 1753:
1754: return (1);
1.1 canacar 1755: }