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