1/* $OpenBSD: pftop.c,v 1.47 2024/04/22 14:19:48 jsg Exp $	 */
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>
39#include <netinet/tcp.h>
40#include <netinet/tcp_fsm.h>
41#include <net/pfvar.h>
42#include <arpa/inet.h>
43
44#include <net/hfsc.h>
45
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>
57#include <limits.h>
58#include <stdarg.h>
59
60#include "systat.h"
61#include "engine.h"
62#include "cache.h"
63
64extern 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 */
80int select_states(void);
81int read_states(void);
82void sort_states(void);
83void print_states(void);
84
85int select_rules(void);
86int read_rules(void);
87void print_rules(void);
88
89int select_queues(void);
90int read_queues(void);
91void print_queues(void);
92
93void update_cache(void);
94
95/* qsort callbacks */
96int sort_size_callback(const void *s1, const void *s2);
97int sort_exp_callback(const void *s1, const void *s2);
98int sort_pkt_callback(const void *s1, const void *s2);
99int sort_age_callback(const void *s1, const void *s2);
100int sort_sa_callback(const void *s1, const void *s2);
101int sort_sp_callback(const void *s1, const void *s2);
102int sort_da_callback(const void *s1, const void *s2);
103int sort_dp_callback(const void *s1, const void *s2);
104int sort_rate_callback(const void *s1, const void *s2);
105int sort_peak_callback(const void *s1, const void *s2);
106int pf_dev = -1;
107
108struct sc_ent **state_cache = NULL;
109struct pfsync_state *state_buf = NULL;
110size_t state_buf_len = 0;
111size_t *state_ord = NULL;
112size_t num_states = 0;
113size_t num_states_all = 0;
114u_int32_t num_rules = 0;
115u_int32_t num_queues = 0;
116int cachestates = 0;
117
118char *filter_string = NULL;
119
120#define MIN_LABEL_SIZE 5
121#define ANCHOR_FLD_SIZE 12
122
123/* Define fields */
124field_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},
143	{"IF", 4, 7, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
144	{"INFO", 40, 80, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
145	{"MAX", 3, 5, 2, FLD_ALIGN_RIGHT, -1, 0, 0},
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},
149	{"ANCHOR", 6, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0},
150	{"QUEUE", 15, 30, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
151	{"BW/FL", 4, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
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 */
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)
170/* common */
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)
176/* for rules */
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)
187/* other */
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)
192/* for queues */
193#define FLD_QUEUE   FIELD_ADDR(fields,25)
194#define FLD_BANDW   FIELD_ADDR(fields,26)
195#define FLD_SCHED   FIELD_ADDR(fields,27)
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)
203
204/* Define views */
205field_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
210field_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
215field_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
220field_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
225field_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
230field_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
236field_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
242field_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
247field_def *view8[] = {
248	FLD_QUEUE, FLD_BANDW, FLD_SCHED, FLD_PKTS, FLD_BYTES,
249	FLD_DROPP, FLD_DROPB, FLD_QLEN, FLD_BORR, FLD_SUSP, FLD_PKTSPS,
250	FLD_BYTESPS, NULL
251};
252
253/* Define orderings */
254order_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 */
270struct view_manager state_mgr = {
271	"States", select_states, read_states, sort_states, print_header,
272	print_states, keyboard_callback, order_list, order_list
273};
274
275struct view_manager rule_mgr = {
276	"Rules", select_rules, read_rules, NULL, print_header,
277	print_rules, keyboard_callback, NULL, NULL
278};
279
280struct view_manager queue_mgr = {
281	"Queues", select_queues, read_queues, NULL, print_header,
282	print_queues, keyboard_callback, NULL, NULL
283};
284
285field_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
292/* queue structures from pfctl */
293
294struct queue_stats {
295	struct hfsc_class_stats	 data;
296	int			 valid;
297	struct timeval		 timestamp;
298};
299
300struct 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};
307TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes);
308
309/* ordering functions */
310
311int
312sort_size_callback(const void *s1, const void *s2)
313{
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]);
318	if (b2 > b1)
319		return sortdir;
320	if (b2 < b1)
321		return -sortdir;
322	return 0;
323}
324
325int
326sort_pkt_callback(const void *s1, const void *s2)
327{
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]);
332	if (p2 > p1)
333		return sortdir;
334	if (p2 < p1)
335		return -sortdir;
336	return 0;
337}
338
339int
340sort_age_callback(const void *s1, const void *s2)
341{
342	if (ntohl(state_buf[* (size_t *) s2].creation) >
343	    ntohl(state_buf[* (size_t *) s1].creation))
344		return sortdir;
345	if (ntohl(state_buf[* (size_t *) s2].creation) <
346	    ntohl(state_buf[* (size_t *) s1].creation))
347		return -sortdir;
348	return 0;
349}
350
351int
352sort_exp_callback(const void *s1, const void *s2)
353{
354	if (ntohl(state_buf[* (size_t *) s2].expire) >
355	    ntohl(state_buf[* (size_t *) s1].expire))
356		return sortdir;
357	if (ntohl(state_buf[* (size_t *) s2].expire) <
358	    ntohl(state_buf[* (size_t *) s1].expire))
359		return -sortdir;
360	return 0;
361}
362
363int
364sort_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;
373
374	if (e2->rate > e1 -> rate)
375		return sortdir;
376	if (e2->rate < e1 -> rate)
377		return -sortdir;
378	return 0;
379}
380
381int
382sort_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;
391
392	if (e2->peak > e1 -> peak)
393		return sortdir;
394	if (e2->peak < e1 -> peak)
395		return -sortdir;
396	return 0;
397}
398
399int
400compare_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	}
428
429	return 0;
430}
431
432static __inline int
433sort_addr_callback(const struct pfsync_state *s1,
434		   const struct pfsync_state *s2, int dir)
435{
436	const struct pf_addr *aa, *ab;
437	u_int16_t pa, pb;
438	int af, side, ret, ii, io;
439
440	side = s1->direction == PF_IN ? PF_SK_STACK : PF_SK_WIRE;
441
442	if (s1->key[side].af > s2->key[side].af)
443		return sortdir;
444	if (s1->key[side].af < s2->key[side].af)
445		return -sortdir;
446
447	ii = io = 0;
448
449	if (dir == PF_OUT)	/* looking for source addr */
450		io = 1;
451	else			/* looking for dest addr */
452		ii = 1;
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) {
463		aa = &s1->key[PF_SK_STACK].addr[ii];
464		pa =  s1->key[PF_SK_STACK].port[ii];
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;
475	} else {
476		dir = s2->direction;
477		side = PF_SK_WIRE;
478	}
479
480	if (dir == PF_IN) {
481		ab = &s2->key[PF_SK_STACK].addr[ii];
482		pb =  s2->key[PF_SK_STACK].port[ii];
483		af = s1->key[PF_SK_STACK].af;
484	} else {
485		ab = &s2->key[side].addr[io];
486		pb =  s2->key[side].port[io];
487		af = s1->key[side].af;
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
499static __inline int
500sort_port_callback(const struct pfsync_state *s1,
501		   const struct pfsync_state *s2, int dir)
502{
503	const struct pf_addr *aa, *ab;
504	u_int16_t pa, pb;
505	int af, side, ret, ii, io;
506
507	side = s1->direction == PF_IN ? PF_SK_STACK : PF_SK_WIRE;
508
509	if (s1->key[side].af > s2->key[side].af)
510		return sortdir;
511	if (s1->key[side].af < s2->key[side].af)
512		return -sortdir;
513
514	ii = io = 0;
515
516	if (dir == PF_OUT)	/* looking for source addr */
517		io = 1;
518	else			/* looking for dest addr */
519		ii = 1;
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) {
530		aa = &s1->key[PF_SK_STACK].addr[ii];
531		pa =  s1->key[PF_SK_STACK].port[ii];
532		af = s1->key[PF_SK_STACK].af;
533	} else {
534		aa = &s1->key[side].addr[io];
535		pa =  s1->key[side].port[io];
536		af = s1->key[side].af;
537	}
538
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) {
548		ab = &s2->key[PF_SK_STACK].addr[ii];
549		pb =  s2->key[PF_SK_STACK].port[ii];
550		af = s1->key[PF_SK_STACK].af;
551	} else {
552		ab = &s2->key[side].addr[io];
553		pb =  s2->key[side].port[io];
554		af = s1->key[side].af;
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
569int
570sort_sa_callback(const void *p1, const void *p2)
571{
572	struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
573	struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
574	return sort_addr_callback(s1, s2, PF_OUT);
575}
576
577int
578sort_da_callback(const void *p1, const void *p2)
579{
580	struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
581	struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
582	return sort_addr_callback(s1, s2, PF_IN);
583}
584
585int
586sort_sp_callback(const void *p1, const void *p2)
587{
588	struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
589	struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
590	return sort_port_callback(s1, s2, PF_OUT);
591}
592
593int
594sort_dp_callback(const void *p1, const void *p2)
595{
596	struct pfsync_state *s1 = state_buf + (* (size_t *) p1);
597	struct pfsync_state *s2 = state_buf + (* (size_t *) p2);
598	return sort_port_callback(s1, s2, PF_IN);
599}
600
601void
602sort_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
620	mergesort(state_ord, num_states, sizeof(size_t), ordering->func);
621}
622
623/* state management functions */
624
625void
626alloc_buf(size_t ns)
627{
628	size_t len;
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;
637		state_buf = reallocarray(state_buf, len,
638		    sizeof(struct pfsync_state));
639		state_ord = reallocarray(state_ord, len, sizeof(size_t));
640		state_cache = reallocarray(state_cache, len,
641		    sizeof(struct sc_ent *));
642		if (state_buf == NULL || state_ord == NULL ||
643		    state_cache == NULL)
644			err(1, "realloc");
645		state_buf_len = len;
646	}
647}
648
649int
650select_states(void)
651{
652	num_disp = num_states;
653	return (0);
654}
655
656int
657read_states(void)
658{
659	struct pfioc_states ps;
660	size_t n;
661
662	if (pf_dev == -1)
663		return -1;
664
665	for (;;) {
666		size_t sbytes = state_buf_len * sizeof(struct pfsync_state);
667
668		ps.ps_len = sbytes;
669		ps.ps_states = state_buf;
670
671		if (ioctl(pf_dev, DIOCGETSTATES, &ps) == -1) {
672			error("DIOCGETSTATES");
673		}
674		num_states_all = ps.ps_len / sizeof(struct pfsync_state);
675
676		if (ps.ps_len < sbytes)
677			break;
678
679		alloc_buf(num_states_all);
680	}
681
682	num_states = num_states_all;
683	for (n = 0; n < num_states_all; n++)
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
696int
697unmask(struct pf_addr * m)
698{
699	int i = 31, j = 0, b = 0;
700	u_int32_t tmp;
701
702	while (j < 4 && m->addr32[j] == 0xffffffff) {
703		b += 32;
704		j++;
705	}
706	if (j < 4) {
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
716void
717tb_print_addr(struct pf_addr * addr, struct pf_addr * mask, int af)
718{
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;
726	}
727
728	if (mask != NULL) {
729		if (!PF_AZERO(mask, af))
730			tbprintf("/%u", unmask(mask));
731	}
732}
733
734void
735print_fld_host2(field_def *fld, struct pfsync_state_key *ks,
736		struct pfsync_state_key *kn, int idx)
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
744	int asf = ks->af;
745	int anf = kn->af;
746
747	if (fld == NULL)
748		return;
749
750	if (fld->width < 3) {
751		print_fld_str(fld, "*");
752		return;
753	}
754
755	tb_start();
756	tb_print_addr(as, NULL, asf);
757
758	if (asf == AF_INET)
759		tbprintf(":%u", ps);
760	else
761		tbprintf("[%u]", ps);
762
763	print_fld_tb(fld);
764
765	if (asf != anf || PF_ANEQ(as, an, asf) || ps != pn) {
766		tb_start();
767		tb_print_addr(an, NULL, anf);
768
769		if (anf == AF_INET)
770			tbprintf(":%u", pn);
771		else
772			tbprintf("[%u]", pn);
773		print_fld_tb(FLD_GW);
774	}
775
776}
777
778void
779print_fld_state(field_def *fld, unsigned int proto,
780		unsigned int s1, unsigned int s2)
781{
782	int len;
783
784	if (fld == NULL)
785		return;
786
787	len = fld->width;
788	if (len < 1)
789		return;
790
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
827int
828print_state(struct pfsync_state * s, struct sc_ent * ent)
829{
830	struct pfsync_state_peer *src, *dst;
831	struct protoent *p;
832	u_int64_t sz;
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;
837
838	if (dir == PF_OUT) {
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
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);
860	} else {
861		print_fld_host2(FLD_SRC, &s->key[PF_SK_STACK],
862		    &s->key[PF_SK_WIRE], 0);
863		print_fld_host2(FLD_DEST, &s->key[PF_SK_STACK],
864		    &s->key[PF_SK_WIRE], 1);
865	}
866
867	if (dir == PF_OUT)
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);
873	print_fld_age(FLD_AGE, ntohl(s->creation));
874	print_fld_age(FLD_EXP, ntohl(s->expire));
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);
881	print_fld_rate(FLD_SA, (s->creation) ?
882		       ((double)sz/(double)ntohl(s->creation)) : -1);
883
884	print_fld_uint(FLD_RULE, ntohl(s->rule));
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
894void
895print_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
909struct pf_rule *rules = NULL;
910u_int32_t alloc_rules = 0;
911
912int
913select_rules(void)
914{
915	num_disp = num_rules;
916	return (0);
917}
918
919
920void
921add_rule_alloc(u_int32_t nr)
922{
923	if (nr == 0)
924		return;
925
926	num_rules += nr;
927
928	if (rules == NULL) {
929		rules = reallocarray(NULL, num_rules, sizeof(struct pf_rule));
930		if (rules == NULL)
931			err(1, "malloc");
932		alloc_rules = num_rules;
933	} else if (num_rules > alloc_rules) {
934		rules = reallocarray(rules, num_rules, sizeof(struct pf_rule));
935		if (rules == NULL)
936			err(1, "realloc");
937		alloc_rules = num_rules;
938	}
939}
940
941int label_length;
942
943void
944close_pf_trans(u_int32_t ticket)
945{
946	if (ioctl(pf_dev, DIOCXEND, &ticket) == -1)
947		error("DIOCXEND: %s", strerror(errno));
948}
949
950int
951read_anchor_rules(char *anchor)
952{
953	struct pfioc_rule pr;
954	u_int32_t nr, num, off;
955	int len;
956
957	if (pf_dev < 0)
958		return (-1);
959
960	memset(&pr, 0, sizeof(pr));
961	strlcpy(pr.anchor, anchor, sizeof(pr.anchor));
962
963	if (ioctl(pf_dev, DIOCGETRULES, &pr) == -1) {
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;
974		if (ioctl(pf_dev, DIOCGETRULE, &pr) == -1) {
975			error("DIOCGETRULE: %s", strerror(errno));
976			close_pf_trans(pr.ticket);
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;
982		len = strlen(pr.rule.label);
983		if (len > label_length)
984			label_length = len;
985		rules[off + nr] = pr.rule;
986	}
987
988	close_pf_trans(pr.ticket);
989
990	return (num);
991}
992
993struct anchor_name {
994	char name[PATH_MAX];
995	struct anchor_name *next;
996	u_int32_t ref;
997};
998
999struct anchor_name *anchor_root = NULL;
1000struct anchor_name *anchor_end = NULL;
1001struct anchor_name *anchor_free = NULL;
1002
1003struct anchor_name*
1004alloc_anchor_name(const char *path)
1005{
1006	struct anchor_name *a;
1007
1008	a = anchor_free;
1009	if (a == NULL) {
1010		a = malloc(sizeof(struct anchor_name));
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
1027void
1028reset_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
1038struct pfioc_ruleset ruleset;
1039char *rs_end = NULL;
1040
1041int
1042read_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
1066	if (ioctl(pf_dev, DIOCGETRULESETS, &ruleset) == -1) {
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;
1085		if (ioctl(pf_dev, DIOCGETRULESET, &ruleset) == -1) {
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
1098void
1099compute_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
1128int
1129read_rules(void)
1130{
1131	int ret, nw, mw;
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
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;
1153	}
1154
1155	num_disp = num_rules;
1156	return (ret);
1157}
1158
1159void
1160tb_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
1181void
1182tb_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
1204void
1205tb_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
1221void
1222tb_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");
1250
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
1270void
1271tb_print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t)
1272{
1273	char	a1[11], a2[11];
1274
1275	snprintf(a1, sizeof(a1), "%u", i1);
1276	snprintf(a2, sizeof(a2), "%u", i2);
1277
1278	tbprintf("%s ", t);
1279	if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE))
1280		tb_print_op(op, "unknown", a2);
1281	else
1282		tb_print_op(op, a1, a2);
1283}
1284
1285void
1286tb_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
1296void
1297print_rule(struct pf_rule *pr)
1298{
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" };
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]);
1316
1317	print_fld_uint(FLD_RULE, pr->nr);
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
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");
1332	else if (pr->keep_state == PF_STATE_SYNPROXY)
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
1339	if (pr->action >= numact)
1340		print_fld_uint(FLD_ACTION, pr->action);
1341	else print_fld_str(FLD_ACTION, actiontypes[pr->action]);
1342
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);
1361
1362	/* print info field */
1363
1364	tb_start();
1365
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	}
1384
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);
1393
1394	if (pr->uid.op)
1395		tb_print_ugid(pr->uid.op, pr->uid.uid[0], pr->uid.uid[1],
1396		        "user");
1397	if (pr->gid.op)
1398		tb_print_ugid(pr->gid.op, pr->gid.gid[0], pr->gid.gid[1],
1399		        "group");
1400
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 ");
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
1436	/* XXX more missing */
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);
1442
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	}
1450
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
1460void
1461print_rules(void)
1462{
1463	u_int32_t n, count = 0;
1464
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 */
1474struct pfctl_queue_node *
1475pfctl_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
1486void
1487pfctl_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
1508int
1509pfctl_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
1525	if (ioctl(pf_dev, DIOCGETQUEUES, &pq) == -1) {
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)
1532		while ((node = TAILQ_FIRST(&qnodes)) != NULL) {
1533			TAILQ_REMOVE(&qnodes, node, entries);
1534			free(node);
1535		}
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);
1544		if (ioctl(pf_dev, DIOCGETQSTATS, &pqs) == -1) {
1545			error("DIOCGETQSTATS: %s", strerror(errno));
1546			return (-1);
1547		}
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		}
1559	}
1560	return (0);
1561}
1562
1563int
1564select_queues(void)
1565{
1566	num_disp = num_queues;
1567	return (0);
1568}
1569
1570int
1571read_queues(void)
1572{
1573	num_disp = num_queues = 0;
1574
1575	if (pfctl_update_qstats() < 0)
1576		return (-1);
1577	num_disp = num_queues;
1578
1579	return(0);
1580}
1581
1582double
1583calc_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
1593double
1594calc_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
1602double
1603calc_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
1611void
1612print_queue_node(struct pfctl_queue_node *node)
1613{
1614	u_int	rate, rtmp;
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);
1623	if (i == 0 && node->qs.ifname[0])
1624		tbprintf(" on %s ", node->qs.ifname);
1625	print_fld_tb(FLD_QUEUE);
1626
1627	// XXX: missing min, max, burst
1628	tb_start();
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)) {
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);
1643	} else
1644		tbprintf("%u%c", rate, unit[i]);
1645	print_fld_tb(FLD_BANDW);
1646
1647	print_fld_str(FLD_SCHED, node->qs.flags & PFQS_FLOWQUEUE ?
1648	    "flow" : "fifo");
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
1679void
1680print_queues(void)
1681{
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;
1698	}
1699}
1700
1701/* main program functions */
1702
1703void
1704update_cache(void)
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
1723int
1724initpftop(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);
1737	} else if (ioctl(pf_dev, DIOCGETSTATUS, &status) == -1) {
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);
1753
1754	return (1);
1755}
1756