File: [local] / src / usr.bin / systat / pftop.c (download)
Revision 1.1, Thu Jun 12 22:26:01 2008 UTC (16 years ago) by canacar
Branch: MAIN
New display engine for systat, based on pftop. Adds new views for pf
(status, state, rule, queue). While all displays work, some keyboard
comands are not implemented yet. Other features include better handling
of display resize and scrolling for long views. Committing now to fix
the remaining issues in the tree.
Testing and comments by otto@ and harding@, ok deraadt@
|
/* $Id: pftop.c,v 1.1 2008/06/12 22:26:01 canacar Exp $ */
/*
* Copyright (c) 2001, 2007 Can Erkin Acar
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp_fsm.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#ifdef HAVE_ALTQ
#include <altq/altq.h>
#include <altq/altq_cbq.h>
#include <altq/altq_priq.h>
#include <altq/altq_hfsc.h>
#endif
#include <ctype.h>
#include <curses.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include "engine.h"
#include "cache.h"
extern const char *tcpstates[];
#define MIN_NUM_STATES 1024
#define NUM_STATE_INC 1024
#define DEFAULT_CACHE_SIZE 10000
#ifndef HAVE_PROTO_NAMES
/* UDP state enumeration */
#define PFUDPS_NSTATES 3 /* number of state levels */
#define PFUDPS_NAMES { \
"NO TRAFFIC", \
"SINGLE", \
"MULTIPLE", \
NULL \
}
/* Other protocol state enumeration */
#define PFOTHERS_NSTATES 3 /* number of state levels */
#define PFOTHERS_NAMES { \
"NO TRAFFIC", \
"SINGLE", \
"MULTIPLE", \
NULL \
}
#endif
#ifdef HAVE_ADDR_WRAP
#ifdef HAVE_ADDR_TYPE
/* XXX must also check type before use */
#define PT_ADDR(x) (&(x)->addr.v.a.addr)
#else
#define PT_ADDR(x) (&(x)->addr.addr)
#endif
#else
#define PT_ADDR(x) (&(x)->addr)
#endif
#ifdef HAVE_ADDR_MASK
#ifdef HAVE_ADDR_TYPE
/* XXX must also check type before use */
#define PT_MASK(x) (&(x)->addr.v.a.mask)
#else
#define PT_MASK(x) (&(x)->addr.mask)
#endif
#else
#define PT_MASK(x) (&(x)->mask)
#endif
#ifdef HAVE_STATE_NOROUTE
#ifdef HAVE_ADDR_TYPE
#define PT_NOROUTE(x) ((x)->addr.type == PF_ADDR_NOROUTE)
#else
#define PT_NOROUTE(x) ((x)->noroute)
#endif
#else
#define PT_NOROUTE(x) (0)
#endif
/* view management */
int select_states(void);
int read_states(void);
void sort_states(void);
void print_states(void);
int select_rules(void);
int read_rules(void);
void print_rules(void);
int print_header(void);
int keyboard_callback(int ch);
#ifdef HAVE_ALTQ
int select_queues(void);
int read_queues(void);
void print_queues(void);
#endif
/* qsort callbacks */
int sort_size_callback(const void *s1, const void *s2);
int sort_exp_callback(const void *s1, const void *s2);
int sort_pkt_callback(const void *s1, const void *s2);
int sort_age_callback(const void *s1, const void *s2);
int sort_sa_callback(const void *s1, const void *s2);
int sort_sp_callback(const void *s1, const void *s2);
int sort_da_callback(const void *s1, const void *s2);
int sort_dp_callback(const void *s1, const void *s2);
int sort_rate_callback(const void *s1, const void *s2);
int sort_peak_callback(const void *s1, const void *s2);
int pf_dev = -1;
struct sc_ent **state_cache = NULL;
pf_state_t *state_buf = NULL;
int state_buf_len = 0;
u_int32_t *state_ord = NULL;
u_int32_t num_states = 0;
u_int32_t num_states_all = 0;
u_int32_t num_rules = 0;
u_int32_t num_queues = 0;
int cachestates = 0;
char *filter_string = NULL;
int dumpfilter = 0;
#ifndef HAVE_RULE_LABELS
#define PF_RULE_LABEL_SIZE 20
#endif
#define MIN_LABEL_SIZE 5
#define ANCHOR_FLD_SIZE 12
/* Define fields */
field_def fields[] = {
{"SRC", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"DEST", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"GW", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"STATE", 5, 23, 18, FLD_ALIGN_COLUMN, -1, 0, 0, 0},
{"AGE", 5, 9, 4, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"EXP", 5, 9, 4, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"PR ", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"DIR", 1, 3, 2, FLD_ALIGN_CENTER, -1, 0, 0, 0},
{"PKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"BYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"RULE", 2, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"LABEL", MIN_LABEL_SIZE, MIN_LABEL_SIZE, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"STATES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"EVAL", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"ACTION", 1, 8, 4, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"LOG", 1, 3, 2, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"QUICK", 1, 1, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"KS", 1, 1, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"IF", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"INFO", 40, 80, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"MAX", 3, 5, 2, FLD_ALIGN_RIGHT, -1, 0, FLD_FLAG_HIDDEN, 0},
{"RATE", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"AVG", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"PEAK", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"ANCHOR", 6, 16, 1, FLD_ALIGN_LEFT, -1, 0, FLD_FLAG_HIDDEN, 0},
{"QUEUE", 15, 30, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"BW", 4, 5, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"SCH", 3, 4, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
{"PRIO", 1, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"DROP_P", 6, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"DROP_B", 6, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"QLEN", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"BORROW", 4, 6, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"SUSPENDS", 4, 6, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"P/S", 3, 7, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
{"B/S", 4, 7, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}
};
#define FIELD_ADDR(x) (&fields[x])
/* for states */
#define FLD_SRC FIELD_ADDR(0)
#define FLD_DEST FIELD_ADDR(1)
#define FLD_GW FIELD_ADDR(2)
#define FLD_STATE FIELD_ADDR(3)
#define FLD_AGE FIELD_ADDR(4)
#define FLD_EXP FIELD_ADDR(5)
/* common */
#define FLD_PROTO FIELD_ADDR(6)
#define FLD_DIR FIELD_ADDR(7)
#define FLD_PKTS FIELD_ADDR(8)
#define FLD_BYTES FIELD_ADDR(9)
#define FLD_RULE FIELD_ADDR(10)
/* for rules */
#define FLD_LABEL FIELD_ADDR(11)
#define FLD_STATS FIELD_ADDR(12)
#define FLD_EVAL FIELD_ADDR(13)
#define FLD_ACTION FIELD_ADDR(14)
#define FLD_LOG FIELD_ADDR(15)
#define FLD_QUICK FIELD_ADDR(16)
#define FLD_KST FIELD_ADDR(17)
#define FLD_IF FIELD_ADDR(18)
#define FLD_RINFO FIELD_ADDR(19)
#define FLD_STMAX FIELD_ADDR(20)
/* other */
#define FLD_SI FIELD_ADDR(21) /* instantaneous speed */
#define FLD_SA FIELD_ADDR(22) /* average speed */
#define FLD_SP FIELD_ADDR(23) /* peak speed */
#define FLD_ANCHOR FIELD_ADDR(24)
/* for queues */
#define FLD_QUEUE FIELD_ADDR(25)
#define FLD_BANDW FIELD_ADDR(26)
#define FLD_SCHED FIELD_ADDR(27)
#define FLD_PRIO FIELD_ADDR(28)
#define FLD_DROPP FIELD_ADDR(29)
#define FLD_DROPB FIELD_ADDR(30)
#define FLD_QLEN FIELD_ADDR(31)
#define FLD_BORR FIELD_ADDR(32)
#define FLD_SUSP FIELD_ADDR(33)
#define FLD_PKTSPS FIELD_ADDR(34)
#define FLD_BYTESPS FIELD_ADDR(35)
/* Define views */
field_def *view0[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_STATE,
FLD_AGE, FLD_EXP, FLD_PKTS, FLD_BYTES, NULL
};
field_def *view1[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_GW, FLD_STATE, FLD_AGE,
FLD_EXP, FLD_PKTS, FLD_BYTES, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, NULL
};
field_def *view2[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_STATE, FLD_AGE, FLD_EXP,
FLD_PKTS, FLD_BYTES, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
};
field_def *view3[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_AGE, FLD_EXP, FLD_PKTS,
FLD_BYTES, FLD_STATE, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
};
field_def *view4[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_PKTS, FLD_BYTES, FLD_STATE,
FLD_AGE, FLD_EXP, FLD_SI, FLD_SP, FLD_SA, FLD_RULE, FLD_GW, NULL
};
field_def *view5[] = {
FLD_RULE, FLD_ANCHOR, FLD_ACTION, FLD_DIR, FLD_LOG, FLD_QUICK, FLD_IF,
FLD_PROTO, FLD_KST, FLD_PKTS, FLD_BYTES, FLD_STATS, FLD_STMAX,
FLD_RINFO, NULL
};
field_def *view6[] = {
FLD_RULE, FLD_LABEL, FLD_PKTS, FLD_BYTES, FLD_STATS, FLD_STMAX,
FLD_ACTION, FLD_DIR, FLD_LOG, FLD_QUICK, FLD_IF, FLD_PROTO,
FLD_ANCHOR, FLD_KST, NULL
};
field_def *view7[] = {
FLD_PROTO, FLD_DIR, FLD_SRC, FLD_DEST, FLD_SI, FLD_SP, FLD_SA,
FLD_BYTES, FLD_STATE, FLD_PKTS, FLD_AGE, FLD_EXP, FLD_RULE, FLD_GW, NULL
};
field_def *view8[] = {
FLD_QUEUE, FLD_BANDW, FLD_SCHED, FLD_PRIO, FLD_PKTS, FLD_BYTES,
FLD_DROPP, FLD_DROPB, FLD_QLEN, FLD_BORR, FLD_SUSP, FLD_PKTSPS,
FLD_BYTESPS, NULL
};
/* Define orderings */
order_type order_list[] = {
{"none", "none", 'N', NULL},
{"bytes", "bytes", 'B', sort_size_callback},
{"expiry", "exp", 'E', sort_exp_callback},
{"packets", "pkt", 'P', sort_pkt_callback},
{"age", "age", 'A', sort_age_callback},
{"source addr", "src", 'F', sort_sa_callback},
{"dest. addr", "dest", 'T', sort_da_callback},
{"source port", "sport", 'S', sort_sp_callback},
{"dest. port", "dport", 'D', sort_dp_callback},
{"rate", "rate", 'R', sort_rate_callback},
{"peak", "peak", 'K', sort_peak_callback},
{NULL, NULL, 0, NULL}
};
/* Define view managers */
struct view_manager state_mgr = {
"States", select_states, read_states, sort_states, print_header,
print_states, keyboard_callback, order_list, NULL
};
struct view_manager rule_mgr = {
"Rules", select_rules, read_rules, NULL, print_header,
print_rules, keyboard_callback, NULL, NULL
};
#ifdef HAVE_ALTQ
struct view_manager queue_mgr = {
"Queues", select_queues, read_queues, NULL, print_header,
print_queues, keyboard_callback, NULL, NULL
};
#endif
field_view views[] = {
{view2, "states", '8', &state_mgr},
{view5, "rules", '9', &rule_mgr},
{view8, "queues", 'Q', &queue_mgr},
{NULL, NULL, 0, NULL}
};
/* altq structures from pfctl */
#ifdef HAVE_ALTQ
union class_stats {
class_stats_t cbq_stats;
struct priq_classstats priq_stats;
struct hfsc_classstats hfsc_stats;
};
struct queue_stats {
union class_stats data;
struct timeval timestamp;
u_int8_t valid;
};
struct pf_altq_node {
struct pf_altq altq;
struct pf_altq_node *next;
struct pf_altq_node *children;
struct pf_altq_node *next_flat;
struct queue_stats qstats;
struct queue_stats qstats_last;
u_int8_t depth;
u_int8_t visited;
};
#endif /* HAVE_ALTQ */
/* ordering functions */
int
sort_size_callback(const void *s1, const void *s2)
{
#ifdef HAVE_INOUT_COUNT
u_int64_t b1 = COUNTER(state_buf[* (u_int32_t *) s1].bytes[0]) +
COUNTER(state_buf[* (u_int32_t *) s1].bytes[1]);
u_int64_t b2 = COUNTER(state_buf[* (u_int32_t *) s2].bytes[0]) +
COUNTER(state_buf[* (u_int32_t *) s2].bytes[1]);
#else
u_int64_t b1 = COUNTER(state_buf[* (u_int32_t *) s1].bytes);
u_int64_t b2 = COUNTER(state_buf[* (u_int32_t *) s2].bytes);
#endif
if (b2 > b1)
return sortdir;
if (b2 < b1)
return -sortdir;
return 0;
}
int
sort_pkt_callback(const void *s1, const void *s2)
{
#ifdef HAVE_INOUT_COUNT
u_int64_t p1 = COUNTER(state_buf[* (u_int32_t *) s1].packets[0]) +
COUNTER(state_buf[* (u_int32_t *) s1].packets[1]);
u_int64_t p2 = COUNTER(state_buf[* (u_int32_t *) s2].packets[0]) +
COUNTER(state_buf[* (u_int32_t *) s2].packets[1]);
#else
u_int64_t p1 = COUNTER(state_buf[* (u_int32_t *) s1].packets);
u_int64_t p2 = COUNTER(state_buf[* (u_int32_t *) s2].packets);
#endif
if (p2 > p1)
return sortdir;
if (p2 < p1)
return -sortdir;
return 0;
}
int
sort_age_callback(const void *s1, const void *s2)
{
if (state_buf[* (u_int32_t *) s2].creation >
state_buf[* (u_int32_t *) s1].creation)
return sortdir;
if (state_buf[* (u_int32_t *) s2].creation <
state_buf[* (u_int32_t *) s1].creation)
return -sortdir;
return 0;
}
int
sort_exp_callback(const void *s1, const void *s2)
{
if (state_buf[* (u_int32_t *) s2].expire >
state_buf[* (u_int32_t *) s1].expire)
return sortdir;
if (state_buf[* (u_int32_t *) s2].expire <
state_buf[* (u_int32_t *) s1].expire)
return -sortdir;
return 0;
}
int
sort_rate_callback(const void *s1, const void *s2)
{
struct sc_ent *e1 = state_cache[* (u_int32_t *) s1];
struct sc_ent *e2 = state_cache[* (u_int32_t *) s2];
if (e1 == NULL)
return sortdir;
if (e2 == NULL)
return -sortdir;
if (e2->rate > e1 -> rate)
return sortdir;
if (e2->rate < e1 -> rate)
return -sortdir;
return 0;
}
int
sort_peak_callback(const void *s1, const void *s2)
{
struct sc_ent *e1 = state_cache[* (u_int32_t *) s1];
struct sc_ent *e2 = state_cache[* (u_int32_t *) s2];
if (e2 == NULL)
return -sortdir;
if (e1 == NULL || e2 == NULL)
return 0;
if (e2->peak > e1 -> peak)
return sortdir;
if (e2->peak < e1 -> peak)
return -sortdir;
return 0;
}
int
compare_addr(int af, const struct pf_addr *a, const struct pf_addr *b)
{
switch (af) {
case AF_INET:
if (ntohl(a->addr32[0]) > ntohl(b->addr32[0]))
return 1;
if (a->addr32[0] != b->addr32[0])
return -1;
break;
case AF_INET6:
if (ntohl(a->addr32[0]) > ntohl(b->addr32[0]))
return 1;
if (a->addr32[0] != b->addr32[0])
return -1;
if (ntohl(a->addr32[1]) > ntohl(b->addr32[1]))
return 1;
if (a->addr32[1] != b->addr32[1])
return -1;
if (ntohl(a->addr32[2]) > ntohl(b->addr32[2]))
return 1;
if (a->addr32[2] != b->addr32[2])
return -1;
if (ntohl(a->addr32[3]) > ntohl(b->addr32[3]))
return 1;
if (a->addr32[3] != b->addr32[3])
return -1;
break;
}
return 0;
}
#ifdef HAVE_PFSYNC_KEY
#ifdef __GNUC__
__inline__
#endif
int
sort_addr_callback(const pf_state_t *s1,
const pf_state_t *s2, int dir)
{
const struct pf_addr *aa, *ab;
u_int16_t pa, pb;
int af, ret, ii, io;
af = s1->af;
if (af > s2->af)
return sortdir;
if (af < s2->af)
return -sortdir;
ii = io = 0;
if (dir == PF_OUT) /* looking for source addr */
io = 1;
else /* looking for dest addr */
ii = 1;
if (s1->direction == PF_IN) {
aa = &s1->key[PF_SK_STACK].addr[ii];
pa = s1->key[PF_SK_STACK].port[ii];
} else {
aa = &s1->key[PF_SK_WIRE].addr[io];
pa = s1->key[PF_SK_WIRE].port[io];
}
if (s2->direction == PF_IN) {
ab = &s2->key[PF_SK_STACK].addr[ii];;
pb = s2->key[PF_SK_STACK].port[ii];
} else {
ab = &s2->key[PF_SK_WIRE].addr[io];;
pb = s2->key[PF_SK_WIRE].port[io];
}
ret = compare_addr(af, aa, ab);
if (ret)
return ret * sortdir;
if (ntohs(pa) > ntohs(pb))
return sortdir;
return -sortdir;
}
#ifdef __GNUC__
__inline__
#endif
int
sort_port_callback(const pf_state_t *s1,
const pf_state_t *s2, int dir)
{
const struct pf_addr *aa, *ab;
u_int16_t pa, pb;
int af, ret, ii, io;
af = s1->af;
if (af > s2->af)
return sortdir;
if (af < s2->af)
return -sortdir;
ii = io = 0;
if (dir == PF_OUT) /* looking for source addr */
io = 1;
else /* looking for dest addr */
ii = 1;
if (s1->direction == PF_IN) {
aa = &s1->key[PF_SK_STACK].addr[ii];
pa = s1->key[PF_SK_STACK].port[ii];
} else {
aa = &s1->key[PF_SK_WIRE].addr[io];
pa = s1->key[PF_SK_WIRE].port[io];
}
if (s2->direction == PF_IN) {
ab = &s2->key[PF_SK_STACK].addr[ii];;
pb = s2->key[PF_SK_STACK].port[ii];
} else {
ab = &s2->key[PF_SK_WIRE].addr[io];;
pb = s2->key[PF_SK_WIRE].port[io];
}
if (ntohs(pa) > ntohs(pb))
return sortdir;
if (ntohs(pa) < ntohs(pb))
return - sortdir;
ret = compare_addr(af, aa, ab);
if (ret)
return ret * sortdir;
return -sortdir;
}
#else /* HAVE_PFSYNC_KEY */
#ifdef __GNUC__
__inline__
#endif
int
sort_addr_callback(const pf_state_t *s1,
const pf_state_t *s2, int dir)
{
const pf_state_host_t *a, *b;
int af, ret;
af = s1->af;
if (af > s2->af)
return sortdir;
if (af < s2->af)
return -sortdir;
if (s1->direction == dir) {
a = &s1->lan;
} else {
a = &s1->ext;
}
if (s2->direction == dir) {
b = &s2->lan;
} else {
b = &s2->ext;
}
ret = compare_addr(af, &a->addr, &b->addr);
if (ret)
return ret * sortdir;
if (ntohs(a->port) > ntohs(b->port))
return sortdir;
return -sortdir;
}
#ifdef __GNUC__
__inline__
#endif
int
sort_port_callback(const pf_state_t *s1,
const pf_state_t *s2, int dir)
{
const pf_state_host_t *a, *b;
int af;
af = s1->af;
if (af > s2->af)
return sortdir;
if (af < s2->af)
return -sortdir;
if (s1->direction == dir) {
a = &s1->lan;
} else {
a = &s1->ext;
}
if (s2->direction == dir) {
b = &s2->lan;
} else {
b = &s2->ext;
}
if (ntohs(a->port) > ntohs(b->port))
return sortdir;
if (ntohs(a->port) < ntohs(b->port))
return -sortdir;
if (compare_addr(af, &a->addr, &b->addr) > 0)
return sortdir;
return -sortdir;
}
#endif /* HAVE_PFSYNC_KEY */
int sort_sa_callback(const void *p1, const void *p2)
{
pf_state_t *s1 = state_buf + (* (u_int32_t *) p1);
pf_state_t *s2 = state_buf + (* (u_int32_t *) p2);
return sort_addr_callback(s1, s2, PF_OUT);
}
int sort_da_callback(const void *p1, const void *p2)
{
pf_state_t *s1 = state_buf + (* (u_int32_t *) p1);
pf_state_t *s2 = state_buf + (* (u_int32_t *) p2);
return sort_addr_callback(s1, s2, PF_IN);
}
int
sort_sp_callback(const void *p1, const void *p2)
{
pf_state_t *s1 = state_buf + (* (u_int32_t *) p1);
pf_state_t *s2 = state_buf + (* (u_int32_t *) p2);
return sort_port_callback(s1, s2, PF_OUT);
}
int
sort_dp_callback(const void *p1, const void *p2)
{
pf_state_t *s1 = state_buf + (* (u_int32_t *) p1);
pf_state_t *s2 = state_buf + (* (u_int32_t *) p2);
return sort_port_callback(s1, s2, PF_IN);
}
void
sort_states(void)
{
order_type *ordering;
if (curr_mgr == NULL)
return;
ordering = curr_mgr->order_curr;
if (ordering == NULL)
return;
if (ordering->func == NULL)
return;
if (state_buf == NULL)
return;
if (num_states <= 0)
return;
mergesort(state_ord, num_states, sizeof(u_int32_t), ordering->func);
}
/* state management functions */
void
alloc_buf(int ns)
{
int len;
if (ns < MIN_NUM_STATES)
ns = MIN_NUM_STATES;
len = ns;
if (len >= state_buf_len) {
len += NUM_STATE_INC;
state_buf = realloc(state_buf, len * sizeof(pf_state_t));
state_ord = realloc(state_ord, len * sizeof(u_int32_t));
state_cache = realloc(state_cache,
len * sizeof(struct sc_ent *));
if (state_buf == NULL || state_ord == NULL ||
state_cache == NULL)
err(1, "realloc");
state_buf_len = len;
}
}
int
select_states(void)
{
num_disp = num_states;
return (0);
}
int
read_states(void)
{
struct pfioc_states ps;
int n;
if (pf_dev == -1)
return -1;
for (;;) {
int sbytes = state_buf_len * sizeof(pf_state_t);
ps.ps_len = sbytes;
ps.ps_buf = (char *) state_buf;
if (ioctl(pf_dev, DIOCGETSTATES, &ps) < 0) {
error("DIOCGETSTATES");
}
num_states_all = ps.ps_len / sizeof(pf_state_t);
if (ps.ps_len < sbytes)
break;
alloc_buf(num_states_all);
}
if (dumpfilter) {
int fd = open("state.dmp", O_WRONLY|O_CREAT|O_EXCL, 0);
if (fd > 0) {
write(fd, state_buf, ps.ps_len);
close(fd);
}
}
num_states = num_states_all;
for (n = 0; n<num_states_all; n++)
state_ord[n] = n;
if (cachestates) {
for (n = 0; n < num_states; n++)
state_cache[n] = cache_state(state_buf + n);
cache_endupdate();
}
num_disp = num_states;
return 0;
}
int
unmask(struct pf_addr * m, u_int8_t af)
{
int i = 31, j = 0, b = 0, msize;
u_int32_t tmp;
if (af == AF_INET)
msize = 1;
else
msize = 4;
while (j < msize && m->addr32[j] == 0xffffffff) {
b += 32;
j++;
}
if (j < msize) {
tmp = ntohl(m->addr32[j]);
for (i = 31; tmp & (1 << i); --i)
b++;
}
return (b);
}
/* display functions */
void
tb_print_addr(struct pf_addr * addr, struct pf_addr * mask, int af)
{
static char buf[48];
const char *bf;
bf = inet_ntop(af, addr, buf, sizeof(buf));
tbprintf("%s", bf);
if (mask != NULL) {
if (!PF_AZERO(mask, af))
tbprintf("/%u", unmask(mask, af));
}
}
#ifdef HAVE_PFSYNC_KEY
void
print_fld_host2(field_def *fld, struct pfsync_state_key *ks,
struct pfsync_state_key *kn, int idx, int af)
{
struct pf_addr *as = &ks->addr[idx];
struct pf_addr *an = &kn->addr[idx];
u_int16_t ps = ntohs(ks->port[idx]);
u_int16_t pn = ntohs(kn->port[idx]);
if (fld == NULL)
return;
if (fld->width < 3) {
print_fld_str(fld, "*");
return;
}
tb_start();
tb_print_addr(as, NULL, af);
if (af == AF_INET)
tbprintf(":%u", ps);
else
tbprintf("[%u]", ps);
print_fld_tb(fld);
if (PF_ANEQ(as, an, af) || ps != pn) {
tb_start();
tb_print_addr(an, NULL, af);
if (af == AF_INET)
tbprintf(":%u", pn);
else
tbprintf("[%u]", pn);
print_fld_tb(FLD_GW);
}
}
#else
void
print_fld_host(field_def *fld, pf_state_host_t * h, int af)
{
u_int16_t p = ntohs(h->port);
if (fld == NULL)
return;
if (fld->width < 3) {
print_fld_str(fld, "*");
return;
}
tb_start();
tb_print_addr(&h->addr, NULL, af);
if (af == AF_INET)
tbprintf(":%u", p);
else
tbprintf("[%u]", p);
print_fld_tb(fld);
}
#endif
void
print_fld_state(field_def *fld, unsigned int proto,
unsigned int s1, unsigned int s2)
{
int len;
if (fld == NULL)
return;
len = fld->width;
if (len < 1)
return;
tb_start();
if (proto == IPPROTO_TCP) {
if (s1 <= TCPS_TIME_WAIT && s2 <= TCPS_TIME_WAIT)
tbprintf("%s:%s", tcpstates[s1], tcpstates[s2]);
#ifdef PF_TCPS_PROXY_SRC
else if (s1 == PF_TCPS_PROXY_SRC ||
s2 == PF_TCPS_PROXY_SRC)
tbprintf("PROXY:SRC\n");
else if (s1 == PF_TCPS_PROXY_DST ||
s2 == PF_TCPS_PROXY_DST)
tbprintf("PROXY:DST\n");
#endif
else
tbprintf("<BAD STATE LEVELS>");
} else if (proto == IPPROTO_UDP && s1 < PFUDPS_NSTATES &&
s2 < PFUDPS_NSTATES) {
const char *states[] = PFUDPS_NAMES;
tbprintf("%s:%s", states[s1], states[s2]);
} else if (proto != IPPROTO_ICMP && s1 < PFOTHERS_NSTATES &&
s2 < PFOTHERS_NSTATES) {
/* XXX ICMP doesn't really have state levels */
const char *states[] = PFOTHERS_NAMES;
tbprintf("%s:%s", states[s1], states[s2]);
} else {
tbprintf("%u:%u", s1, s2);
}
if (strlen(tmp_buf) > len) {
tb_start();
tbprintf("%u:%u", s1, s2);
}
print_fld_tb(fld);
}
int
print_state(pf_state_t * s, struct sc_ent * ent)
{
pf_state_peer_t *src, *dst;
struct protoent *p;
if (s->direction == PF_OUT) {
src = &s->src;
dst = &s->dst;
} else {
src = &s->dst;
dst = &s->src;
}
p = getprotobynumber(s->proto);
if (p != NULL)
print_fld_str(FLD_PROTO, p->p_name);
else
print_fld_uint(FLD_PROTO, s->proto);
#ifdef HAVE_PFSYNC_KEY
if (s->direction == PF_OUT) {
print_fld_host2(FLD_SRC, &s->key[PF_SK_WIRE],
&s->key[PF_SK_STACK], 1, s->af);
print_fld_host2(FLD_DEST, &s->key[PF_SK_WIRE],
&s->key[PF_SK_STACK], 0, s->af);
} else {
print_fld_host2(FLD_SRC, &s->key[PF_SK_STACK],
&s->key[PF_SK_WIRE], 0, s->af);
print_fld_host2(FLD_DEST, &s->key[PF_SK_STACK],
&s->key[PF_SK_WIRE], 1, s->af);
}
#else
if (s->direction == PF_OUT) {
print_fld_host(FLD_SRC, &s->lan, s->af);
print_fld_host(FLD_DEST, &s->ext, s->af);
} else {
print_fld_host(FLD_SRC, &s->ext, s->af);
print_fld_host(FLD_DEST, &s->lan, s->af);
}
if (PF_ANEQ(&s->lan.addr, &s->gwy.addr, s->af) ||
(s->lan.port != s->gwy.port)) {
print_fld_host(FLD_GW, &s->gwy, s->af);
}
#endif
if (s->direction == PF_OUT)
print_fld_str(FLD_DIR, "Out");
else
print_fld_str(FLD_DIR, "In");
print_fld_state(FLD_STATE, s->proto, src->state, dst->state);
print_fld_age(FLD_AGE, s->creation);
print_fld_age(FLD_EXP, s->expire);
#ifdef HAVE_INOUT_COUNT
{
u_int64_t sz = COUNTER(s->bytes[0]) + COUNTER(s->bytes[1]);
print_fld_size(FLD_PKTS, COUNTER(s->packets[0]) +
COUNTER(s->packets[1]));
print_fld_size(FLD_BYTES, sz);
print_fld_rate(FLD_SA, (s->creation > 0) ?
((double)sz/(double)s->creation) : -1);
}
#else
print_fld_size(FLD_PKTS, s->packets);
print_fld_size(FLD_BYTES, s->bytes);
print_fld_rate(FLD_SA, (s->creation > 0) ?
((double)s->bytes/(double)s->creation) : -1);
#endif
#ifdef HAVE_PFSYNC_STATE
print_fld_uint(FLD_RULE, s->rule);
#else
#ifdef HAVE_RULE_NUMBER
print_fld_uint(FLD_RULE, s->rule.nr);
#endif
#endif
if (cachestates && ent != NULL) {
print_fld_rate(FLD_SI, ent->rate);
print_fld_rate(FLD_SP, ent->peak);
}
end_line();
return 1;
}
void
print_states(void)
{
int n, count = 0;
for (n = dispstart; n < num_disp; n++) {
count += print_state(state_buf + state_ord[n],
state_cache[state_ord[n]]);
if (maxprint > 0 && count >= maxprint)
break;
}
}
/* rule display */
struct pf_rule *rules = NULL;
u_int32_t alloc_rules = 0;
int
select_rules(void)
{
num_disp = num_rules;
return (0);
}
void
add_rule_alloc(u_int32_t nr)
{
if (nr == 0)
return;
num_rules += nr;
if (rules == NULL) {
rules = malloc(num_rules * sizeof(struct pf_rule));
if (rules == NULL)
err(1, "malloc");
alloc_rules = num_rules;
} else if (num_rules > alloc_rules) {
rules = realloc(rules, num_rules * sizeof(struct pf_rule));
if (rules == NULL)
err(1, "realloc");
alloc_rules = num_rules;
}
}
#ifdef HAVE_RULE_LABELS
int label_length;
#endif
int
read_anchor_rules(char *anchor)
{
struct pfioc_rule pr;
u_int32_t nr, num, off;
if (pf_dev < 0)
return (-1);
memset(&pr, 0, sizeof(pr));
#ifdef HAVE_RULESETS
strlcpy(pr.anchor, anchor, sizeof(pr.anchor));
#endif
if (ioctl(pf_dev, DIOCGETRULES, &pr)) {
error("anchor %s: %s", anchor, strerror(errno));
return (-1);
}
off = num_rules;
num = pr.nr;
add_rule_alloc(num);
for (nr = 0; nr < num; ++nr) {
pr.nr = nr;
if (ioctl(pf_dev, DIOCGETRULE, &pr)) {
error("DIOCGETRULE: %s", strerror(errno));
return (-1);
}
#ifdef HAVE_RULESETS
/* XXX overload pr.anchor, to store a pointer to
* anchor name */
pr.rule.anchor = (struct pf_anchor *) anchor;
#endif
#ifdef HAVE_RULE_LABELS
{
int len = strlen(pr.rule.label);
if (len > label_length)
label_length = len;
}
#endif
rules[off + nr] = pr.rule;
}
return (num);
}
#ifdef HAVE_RULESETS
struct anchor_name {
char name[MAXPATHLEN];
struct anchor_name *next;
u_int32_t ref;
};
struct anchor_name *anchor_root = NULL;
struct anchor_name *anchor_end = NULL;
struct anchor_name *anchor_free = NULL;
struct anchor_name*
alloc_anchor_name(const char *path)
{
struct anchor_name *a;
a = anchor_free;
if (a == NULL) {
a = (struct anchor_name *)malloc(sizeof(struct anchor_name));
if (a == NULL)
return (NULL);
} else
anchor_free = a->next;
if (anchor_root == NULL)
anchor_end = a;
a->next = anchor_root;
anchor_root = a;
a->ref = 0;
strlcpy(a->name, path, sizeof(a->name));
return (a);
}
void
reset_anchor_names(void)
{
if (anchor_end == NULL)
return;
anchor_end->next = anchor_free;
anchor_free = anchor_root;
anchor_root = anchor_end = NULL;
}
struct pfioc_ruleset ruleset;
char *rs_end = NULL;
int
read_rulesets(const char *path)
{
char *pre;
struct anchor_name *a;
u_int32_t nr, ns;
int len;
if (path == NULL)
ruleset.path[0] = '\0';
else if (strlcpy(ruleset.path, path, sizeof(ruleset.path)) >=
sizeof(ruleset.path))
return (-1);
/* a persistent storage for anchor names */
a = alloc_anchor_name(ruleset.path);
if (a == NULL)
return (-1);
len = read_anchor_rules(a->name);
if (len < 0)
return (-1);
a->ref += len;
if (ioctl(pf_dev, DIOCGETRULESETS, &ruleset)) {
error("DIOCGETRULESETS: %s", strerror(errno));
return (-1);
}
ns = ruleset.nr;
if (rs_end == NULL)
rs_end = ruleset.path + sizeof(ruleset.path);
/* 'pre' tracks the previous level on the anchor */
pre = strchr(ruleset.path, 0);
len = rs_end - pre;
if (len < 1)
return (-1);
--len;
for (nr = 0; nr < ns; ++nr) {
ruleset.nr = nr;
if (ioctl(pf_dev, DIOCGETRULESET, &ruleset)) {
error("DIOCGETRULESET: %s", strerror(errno));
return (-1);
}
*pre = '/';
if (strlcpy(pre + 1, ruleset.name, len) < len)
read_rulesets(ruleset.path);
*pre = '\0';
}
return (0);
}
void
compute_anchor_field(void)
{
struct anchor_name *a;
int sum, cnt, mx, nx;
sum = cnt = mx = 0;
for (a = anchor_root; a != NULL; a = a->next, cnt++) {
int len;
if (a->ref == 0)
continue;
len = strlen(a->name);
sum += len;
if (len > mx)
mx = len;
}
nx = sum/cnt;
if (nx < ANCHOR_FLD_SIZE)
nx = (mx < ANCHOR_FLD_SIZE) ? mx : ANCHOR_FLD_SIZE;
if (FLD_ANCHOR->max_width != mx ||
FLD_ANCHOR->norm_width != nx) {
FLD_ANCHOR->max_width = mx;
FLD_ANCHOR->norm_width = nx;
field_setup();
need_update = 1;
}
}
#endif
int
read_rules(void)
{
int ret;
num_rules = 0;
if (pf_dev == -1)
return (-1);
#ifdef HAVE_RULE_LABELS
label_length = MIN_LABEL_SIZE;
#endif
#ifdef HAVE_RULESETS
reset_anchor_names();
ret = read_rulesets(NULL);
compute_anchor_field();
#else
ret = read_anchor_rules(NULL);
#endif
#ifdef HAVE_RULE_LABELS
{
int nw, mw;
nw = mw = label_length;
if (nw > 16)
nw = 16;
if (FLD_LABEL->norm_width != nw ||
FLD_LABEL->max_width != mw) {
FLD_LABEL->norm_width = nw;
FLD_LABEL->max_width = mw;
field_setup();
need_update = 1;
}
}
#endif
num_disp = num_rules;
return (ret);
}
#ifdef HAVE_ADDR_WRAP
void
tb_print_addrw(struct pf_addr_wrap *addr, struct pf_addr *mask, u_int8_t af)
{
#ifdef HAVE_ADDR_TYPE
switch (addr->type) {
case PF_ADDR_ADDRMASK:
tb_print_addr(&addr->v.a.addr, mask, af);
break;
case PF_ADDR_NOROUTE:
tbprintf("noroute");
break;
case PF_ADDR_DYNIFTL:
tbprintf("(%s)", addr->v.ifname);
break;
case PF_ADDR_TABLE:
tbprintf("<%s>", addr->v.tblname);
break;
default:
tbprintf("UNKNOWN");
break;
}
#else
if (addr->addr_dyn != NULL)
tbprintf("(%s)", addr->addr.pfa.ifname);
else
tb_print_addr(&addr->addr, mask, af);
#endif
}
#endif
void
tb_print_op(u_int8_t op, const char *a1, const char *a2)
{
if (op == PF_OP_IRG)
tbprintf("%s >< %s ", a1, a2);
else if (op == PF_OP_XRG)
tbprintf("%s <> %s ", a1, a2);
#ifdef HAVE_OP_RRG
else if (op == PF_OP_RRG)
tbprintf("%s:%s ", a1, a2);
#endif
else if (op == PF_OP_EQ)
tbprintf("= %s ", a1);
else if (op == PF_OP_NE)
tbprintf("!= %s ", a1);
else if (op == PF_OP_LT)
tbprintf("< %s ", a1);
else if (op == PF_OP_LE)
tbprintf("<= %s ", a1);
else if (op == PF_OP_GT)
tbprintf("> %s ", a1);
else if (op == PF_OP_GE)
tbprintf(">= %s ", a1);
}
void
tb_print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, char *proto)
{
char a1[6], a2[6];
struct servent *s = getservbyport(p1, proto);
p1 = ntohs(p1);
p2 = ntohs(p2);
snprintf(a1, sizeof(a1), "%u", p1);
snprintf(a2, sizeof(a2), "%u", p2);
tbprintf("port ");
if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE))
tb_print_op(op, s->s_name, a2);
else
tb_print_op(op, a1, a2);
}
void
tb_print_fromto(struct pf_rule_addr *src, struct pf_rule_addr *dst,
u_int8_t af, u_int8_t proto)
{
if (
PF_AZERO(PT_ADDR(src), AF_INET6) &&
PF_AZERO(PT_ADDR(dst), AF_INET6) &&
! PT_NOROUTE(src) && ! PT_NOROUTE(dst) &&
PF_AZERO(PT_MASK(src), AF_INET6) &&
PF_AZERO(PT_MASK(dst), AF_INET6) &&
!src->port_op && !dst->port_op)
tbprintf("all ");
else {
tbprintf("from ");
if (PT_NOROUTE(src))
tbprintf("no-route ");
else if (PF_AZERO(PT_ADDR(src), AF_INET6) &&
PF_AZERO(PT_MASK(src), AF_INET6))
tbprintf("any ");
else {
#ifdef HAVE_NEG
if (src->neg)
#else
if (src->not)
#endif
tbprintf("! ");
#ifdef HAVE_ADDR_WRAP
tb_print_addrw(&src->addr, PT_MASK(src), af);
#else
tb_print_addr(&src->addr, PT_MASK(src), af);
#endif
tbprintf(" ");
}
if (src->port_op)
tb_print_port(src->port_op, src->port[0],
src->port[1],
proto == IPPROTO_TCP ? "tcp" : "udp");
tbprintf("to ");
if (PT_NOROUTE(dst))
tbprintf("no-route ");
else if (PF_AZERO(PT_ADDR(dst), AF_INET6) &&
PF_AZERO(PT_MASK(dst), AF_INET6))
tbprintf("any ");
else {
#ifdef HAVE_NEG
if (dst->neg)
#else
if (dst->not)
#endif
tbprintf("! ");
#ifdef HAVE_ADDR_WRAP
tb_print_addrw(&dst->addr, PT_MASK(dst), af);
#else
tb_print_addr(&dst->addr, PT_MASK(dst), af);
#endif
tbprintf(" ");
}
if (dst->port_op)
tb_print_port(dst->port_op, dst->port[0],
dst->port[1],
proto == IPPROTO_TCP ? "tcp" : "udp");
}
}
#ifdef HAVE_RULE_UGID
void
tb_print_ugid(u_int8_t op, unsigned u1, unsigned u2,
const char *t, unsigned umax)
{
char a1[11], a2[11];
snprintf(a1, sizeof(a1), "%u", u1);
snprintf(a2, sizeof(a2), "%u", u2);
tbprintf("%s ", t);
if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE))
tb_print_op(op, "unknown", a2);
else
tb_print_op(op, a1, a2);
}
#endif
void
tb_print_flags(u_int8_t f)
{
const char *tcpflags = "FSRPAUEW";
int i;
for (i = 0; tcpflags[i]; ++i)
if (f & (1 << i))
tbprintf("%c", tcpflags[i]);
}
void
print_rule(struct pf_rule *pr)
{
static const char *actiontypes[] = { "Pass", "Block", "Scrub", "Nat",
"no Nat", "Binat", "no Binat", "Rdr", "no Rdr" };
int numact = sizeof(actiontypes) / sizeof(char *);
#ifdef HAVE_PF_ROUTE
static const char *routetypes[] = { "", "fastroute", "route-to",
"dup-to", "reply-to" };
int numroute = sizeof(routetypes) / sizeof(char *);
#endif
if (pr == NULL) return;
#ifdef HAVE_RULE_LABELS
print_fld_str(FLD_LABEL, pr->label);
#endif
#ifdef HAVE_RULE_STATES
#ifdef HAVE_PFSYNC_KEY
print_fld_size(FLD_STATS, pr->states_tot);
#else
print_fld_size(FLD_STATS, pr->states);
#endif
#endif
#ifdef HAVE_INOUT_COUNT_RULES
print_fld_size(FLD_PKTS, pr->packets[0] + pr->packets[1]);
print_fld_size(FLD_BYTES, pr->bytes[0] + pr->bytes[1]);
#else
print_fld_size(FLD_PKTS, pr->packets);
print_fld_size(FLD_BYTES, pr->bytes);
#endif
print_fld_uint(FLD_RULE, pr->nr);
print_fld_str(FLD_DIR, pr->direction == PF_OUT ? "Out" : "In");
if (pr->quick)
print_fld_str(FLD_QUICK, "Quick");
if (pr->keep_state == PF_STATE_NORMAL)
print_fld_str(FLD_KST, "Keep");
else if (pr->keep_state == PF_STATE_MODULATE)
print_fld_str(FLD_KST, "Mod");
#ifdef PF_STATE_SYNPROXY
else if (pr->keep_state == PF_STATE_MODULATE)
print_fld_str(FLD_KST, "Syn");
#endif
if (pr->log == 1)
print_fld_str(FLD_LOG, "Log");
else if (pr->log == 2)
print_fld_str(FLD_LOG, "All");
if (pr->action >= numact)
print_fld_uint(FLD_ACTION, pr->action);
else print_fld_str(FLD_ACTION, actiontypes[pr->action]);
if (pr->proto) {
struct protoent *p = getprotobynumber(pr->proto);
if (p != NULL)
print_fld_str(FLD_PROTO, p->p_name);
else
print_fld_uint(FLD_PROTO, pr->proto);
}
if (pr->ifname[0]) {
tb_start();
#ifdef HAVE_RULE_IFNOT
if (pr->ifnot)
tbprintf("!");
#endif
tbprintf("%s", pr->ifname);
print_fld_tb(FLD_IF);
}
#ifdef HAVE_MAX_STATES
if (pr->max_states)
print_fld_uint(FLD_STMAX, pr->max_states);
#endif
/* print info field */
tb_start();
#ifdef HAVE_RULE_NATPASS
if (pr->natpass)
tbprintf("pass ");
#endif
if (pr->action == PF_DROP) {
if (pr->rule_flag & PFRULE_RETURNRST)
tbprintf("return-rst ");
#ifdef PFRULE_RETURN
else if (pr->rule_flag & PFRULE_RETURN)
tbprintf("return ");
#endif
#ifdef PFRULE_RETURNICMP
else if (pr->rule_flag & PFRULE_RETURNICMP)
tbprintf("return-icmp ");
#endif
else
tbprintf("drop ");
}
#ifdef HAVE_PF_ROUTE
if (pr->rt > 0 && pr->rt < numroute) {
tbprintf("%s ", routetypes[pr->rt]);
if (pr->rt != PF_FASTROUTE)
tbprintf("... ");
}
#endif
if (pr->af) {
if (pr->af == AF_INET)
tbprintf("inet ");
else
tbprintf("inet6 ");
}
tb_print_fromto(&pr->src, &pr->dst, pr->af, pr->proto);
#ifdef HAVE_RULE_UGID
if (pr->uid.op)
tb_print_ugid(pr->uid.op, pr->uid.uid[0], pr->uid.uid[1],
"user", UID_MAX);
if (pr->gid.op)
tb_print_ugid(pr->gid.op, pr->gid.gid[0], pr->gid.gid[1],
"group", GID_MAX);
#endif
if (pr->flags || pr->flagset) {
tbprintf(" flags ");
tb_print_flags(pr->flags);
tbprintf("/");
tb_print_flags(pr->flagset);
}
tbprintf(" ");
#ifdef HAVE_RULE_TOS
if (pr->tos)
tbprintf("tos 0x%2.2x ", pr->tos);
#endif
#ifdef PFRULE_FRAGMENT
if (pr->rule_flag & PFRULE_FRAGMENT)
tbprintf("fragment ");
#endif
#ifdef PFRULE_NODF
if (pr->rule_flag & PFRULE_NODF)
tbprintf("no-df ");
#endif
#ifdef PFRULE_RANDOMID
if (pr->rule_flag & PFRULE_RANDOMID)
tbprintf("random-id ");
#endif
if (pr->min_ttl)
tbprintf("min-ttl %d ", pr->min_ttl);
#ifdef HAVE_MAX_MSS
if (pr->max_mss)
tbprintf("max-mss %d ", pr->max_mss);
#endif
if (pr->allow_opts)
tbprintf("allow-opts ");
if (pr->action == PF_SCRUB) {
#ifdef PFRULE_REASSEMBLE_TCP
if (pr->rule_flag & PFRULE_REASSEMBLE_TCP)
tbprintf("reassemble tcp ");
#endif
#ifdef PFRULE_FRAGDROP
if (pr->rule_flag & PFRULE_FRAGDROP)
tbprintf("fragment drop-ovl ");
else
#endif
#ifdef PFRULE_FRAGCROP
if (pr->rule_flag & PFRULE_FRAGCROP)
tbprintf("fragment crop ");
else
#endif
tbprintf("fragment reassemble ");
}
#ifdef HAVE_ALTQ
if (pr->qname[0] && pr->pqname[0])
tbprintf("queue(%s, %s) ", pr->qname, pr->pqname);
else if (pr->qname[0])
tbprintf("queue %s ", pr->qname);
#endif
#ifdef HAVE_TAGS
if (pr->tagname[0])
tbprintf("tag %s ", pr->tagname);
if (pr->match_tagname[0]) {
if (pr->match_tag_not)
tbprintf("! ");
tbprintf("tagged %s ", pr->match_tagname);
}
#endif
print_fld_tb(FLD_RINFO);
#ifdef HAVE_RULESETS
/* XXX anchor field overloaded with anchor name */
print_fld_str(FLD_ANCHOR, (char *)pr->anchor);
#endif
tb_end();
end_line();
}
void
print_rules(void)
{
u_int32_t n, count = 0;
for (n = dispstart; n < num_rules; n++) {
print_rule(rules + n);
count ++;
if (maxprint > 0 && count >= maxprint)
break;
}
}
/* queue display */
#ifdef HAVE_ALTQ
struct pf_altq_node *
pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
const char *ifname)
{
struct pf_altq_node *node, *child;
for (node = root; node != NULL; node = node->next) {
if (!strcmp(node->altq.qname, qname)
&& !(strcmp(node->altq.ifname, ifname)))
return (node);
if (node->children != NULL) {
child = pfctl_find_altq_node(node->children, qname,
ifname);
if (child != NULL)
return (child);
}
}
return (NULL);
}
void
pfctl_insert_altq_node(struct pf_altq_node **root,
const struct pf_altq altq, const struct queue_stats qstats)
{
struct pf_altq_node *node;
node = calloc(1, sizeof(struct pf_altq_node));
if (node == NULL)
err(1, "pfctl_insert_altq_node: calloc");
memcpy(&node->altq, &altq, sizeof(struct pf_altq));
memcpy(&node->qstats, &qstats, sizeof(qstats));
node->next = node->children = node->next_flat = NULL;
node->depth = 0;
node->visited = 1;
if (*root == NULL)
*root = node;
else if (!altq.parent[0]) {
struct pf_altq_node *prev = *root;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
} else {
struct pf_altq_node *parent;
parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
if (parent == NULL)
errx(1, "parent %s not found", altq.parent);
node->depth = parent->depth+1;
if (parent->children == NULL)
parent->children = node;
else {
struct pf_altq_node *prev = parent->children;
while (prev->next != NULL)
prev = prev->next;
prev->next = node;
}
}
if (*root != node) {
struct pf_altq_node *prev_flat = *root;
while (prev_flat->next_flat != NULL) {
prev_flat = prev_flat->next_flat;
}
prev_flat->next_flat = node;
}
}
int
pfctl_update_qstats(struct pf_altq_node **root, int *inserts)
{
struct pf_altq_node *node;
struct pfioc_altq pa;
struct pfioc_qstats pq;
u_int32_t nr;
struct queue_stats qstats;
u_int32_t nr_queues;
*inserts = 0;
memset(&pa, 0, sizeof(pa));
memset(&pq, 0, sizeof(pq));
memset(&qstats, 0, sizeof(qstats));
if (pf_dev < 0)
return (-1);
if (ioctl(pf_dev, DIOCGETALTQS, &pa)) {
error("DIOCGETALTQS: %s", strerror(errno));
return (-1);
}
num_queues = nr_queues = pa.nr;
for (nr = 0; nr < nr_queues; ++nr) {
pa.nr = nr;
if (ioctl(pf_dev, DIOCGETALTQ, &pa)) {
error("DIOCGETALTQ: %s", strerror(errno));
return (-1);
}
if (pa.altq.qid > 0) {
pq.nr = nr;
pq.ticket = pa.ticket;
pq.buf = &qstats;
pq.nbytes = sizeof(qstats);
if (ioctl(pf_dev, DIOCGETQSTATS, &pq)) {
error("DIOCGETQSTATS: %s", strerror(errno));
return (-1);
}
qstats.valid = 1;
gettimeofday(&qstats.timestamp, NULL);
if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
pa.altq.ifname)) != NULL) {
// update altq data too as bandwidth may have changed
memcpy(&node->altq, &pa.altq, sizeof(struct pf_altq));
memcpy(&node->qstats_last, &node->qstats,
sizeof(struct queue_stats));
memcpy(&node->qstats, &qstats,
sizeof(qstats));
node->visited = 1;
} else {
pfctl_insert_altq_node(root, pa.altq, qstats);
*inserts = 1;
}
}
else
--num_queues;
}
return (0);
}
void
pfctl_free_altq_node(struct pf_altq_node *node)
{
while (node != NULL) {
struct pf_altq_node *prev;
if (node->children != NULL)
pfctl_free_altq_node(node->children);
prev = node;
node = node->next;
free(prev);
}
}
void
pfctl_mark_all_unvisited(struct pf_altq_node *root)
{
if (root != NULL) {
struct pf_altq_node *node = root;
while (node != NULL) {
node->visited = 0;
node = node->next_flat;
}
}
}
int
pfctl_have_unvisited(struct pf_altq_node *root)
{
if (root == NULL)
return(0);
else {
struct pf_altq_node *node = root;
while (node != NULL) {
if (node->visited == 0)
return(1);
node = node->next_flat;
}
return(0);
}
}
struct pf_altq_node *altq_root = NULL;
int
select_queues(void)
{
num_disp = num_queues;
return (0);
}
int
read_queues(void)
{
static int first_read = 1;
int inserts;
num_disp = num_queues = 0;
pfctl_mark_all_unvisited(altq_root);
if (pfctl_update_qstats(&altq_root, &inserts))
return (-1);
// Allow inserts only on first read;
// on subsequent reads clear and reload
if (first_read == 0 &&
(inserts != 0 || pfctl_have_unvisited(altq_root) != 0)) {
pfctl_free_altq_node(altq_root);
altq_root = NULL;
first_read = 1;
if (pfctl_update_qstats(&altq_root, &inserts))
return (-1);
}
first_read = 0;
num_disp = num_queues;
return(0);
}
double
calc_interval(struct timeval *cur_time, struct timeval *last_time)
{
double sec;
sec = (double)(cur_time->tv_sec - last_time->tv_sec) +
(double)(cur_time->tv_usec - last_time->tv_usec) / 1000000;
return (sec);
}
double
calc_rate(u_int64_t new_bytes, u_int64_t last_bytes, double interval)
{
double rate;
rate = (double)(new_bytes - last_bytes) / interval;
return (rate);
}
double
calc_pps(u_int64_t new_pkts, u_int64_t last_pkts, double interval)
{
double pps;
pps = (double)(new_pkts - last_pkts) / interval;
return (pps);
}
#define DEFAULT_PRIORITY 1
void
print_queue(struct pf_altq_node *node)
{
u_int8_t d;
double interval, pps, bps;
pps = bps = 0;
tb_start();
for (d = 0; d < node->depth; d++)
tbprintf(" ");
tbprintf(node->altq.qname);
print_fld_tb(FLD_QUEUE);
if (node->altq.scheduler == ALTQT_CBQ ||
node->altq.scheduler == ALTQT_HFSC
)
print_fld_bw(FLD_BANDW, (double)node->altq.bandwidth);
if (node->altq.priority != DEFAULT_PRIORITY)
print_fld_uint(FLD_PRIO,
node->altq.priority);
if (node->qstats.valid && node->qstats_last.valid)
interval = calc_interval(&node->qstats.timestamp,
&node->qstats_last.timestamp);
else
interval = 0;
switch (node->altq.scheduler) {
case ALTQT_CBQ:
print_fld_str(FLD_SCHED, "cbq");
print_fld_size(FLD_PKTS,
node->qstats.data.cbq_stats.xmit_cnt.packets);
print_fld_size(FLD_BYTES,
node->qstats.data.cbq_stats.xmit_cnt.bytes);
print_fld_size(FLD_DROPP,
node->qstats.data.cbq_stats.drop_cnt.packets);
print_fld_size(FLD_DROPB,
node->qstats.data.cbq_stats.drop_cnt.bytes);
print_fld_size(FLD_QLEN, node->qstats.data.cbq_stats.qcnt);
print_fld_size(FLD_BORR, node->qstats.data.cbq_stats.borrows);
print_fld_size(FLD_SUSP, node->qstats.data.cbq_stats.delays);
if (interval > 0) {
pps = calc_pps(node->qstats.data.cbq_stats.xmit_cnt.packets,
node->qstats_last.data.cbq_stats.xmit_cnt.packets, interval);
bps = calc_rate(node->qstats.data.cbq_stats.xmit_cnt.bytes,
node->qstats_last.data.cbq_stats.xmit_cnt.bytes, interval);
}
break;
case ALTQT_PRIQ:
print_fld_str(FLD_SCHED, "priq");
print_fld_size(FLD_PKTS,
node->qstats.data.priq_stats.xmitcnt.packets);
print_fld_size(FLD_BYTES,
node->qstats.data.priq_stats.xmitcnt.bytes);
print_fld_size(FLD_DROPP,
node->qstats.data.priq_stats.dropcnt.packets);
print_fld_size(FLD_DROPB,
node->qstats.data.priq_stats.dropcnt.bytes);
print_fld_size(FLD_QLEN, node->qstats.data.priq_stats.qlength);
if (interval > 0) {
pps = calc_pps(node->qstats.data.priq_stats.xmitcnt.packets,
node->qstats_last.data.priq_stats.xmitcnt.packets, interval);
bps = calc_rate(node->qstats.data.priq_stats.xmitcnt.bytes,
node->qstats_last.data.priq_stats.xmitcnt.bytes, interval);
}
break;
case ALTQT_HFSC:
print_fld_str(FLD_SCHED, "hfsc");
print_fld_size(FLD_PKTS,
node->qstats.data.hfsc_stats.xmit_cnt.packets);
print_fld_size(FLD_BYTES,
node->qstats.data.hfsc_stats.xmit_cnt.bytes);
print_fld_size(FLD_DROPP,
node->qstats.data.hfsc_stats.drop_cnt.packets);
print_fld_size(FLD_DROPB,
node->qstats.data.hfsc_stats.drop_cnt.bytes);
print_fld_size(FLD_QLEN, node->qstats.data.hfsc_stats.qlength);
if (interval > 0) {
pps = calc_pps(node->qstats.data.hfsc_stats.xmit_cnt.packets,
node->qstats_last.data.hfsc_stats.xmit_cnt.packets, interval);
bps = calc_rate(node->qstats.data.hfsc_stats.xmit_cnt.bytes,
node->qstats_last.data.hfsc_stats.xmit_cnt.bytes, interval);
}
break;
}
/* if (node->altq.scheduler != ALTQT_HFSC && interval > 0) { */
if (node->altq.scheduler && interval > 0) {
tb_start();
if (pps > 0 && pps < 1)
tbprintf("%-3.1lf", pps);
else
tbprintf("%u", (unsigned int) pps);
print_fld_tb(FLD_PKTSPS);
print_fld_bw(FLD_BYTESPS, bps);
}
}
void
print_queues(void)
{
u_int32_t n, count = 0;
struct pf_altq_node *node = altq_root;
for (n = 0; n < dispstart; n++)
node = node->next_flat;
for (; n < num_disp; n++) {
print_queue(node);
node = node->next_flat;
end_line();
count ++;
if (maxprint > 0 && count >= maxprint)
break;
}
}
#endif /* HAVE_ALTQ */
/* main program functions */
void
update_cache()
{
static int pstate = -1;
if (pstate == cachestates)
return;
pstate = cachestates;
if (cachestates) {
show_field(FLD_SI);
show_field(FLD_SP);
gotsig_alarm = 1;
} else {
hide_field(FLD_SI);
hide_field(FLD_SP);
need_update = 1;
}
field_setup();
}
void
initpftop(void)
{
struct pf_status status;
field_view *v;
int cachesize = DEFAULT_CACHE_SIZE;
v = views;
while(v->name != NULL)
add_view(v++);
pf_dev = open("/dev/pf", O_RDONLY);
if (pf_dev == -1) {
alloc_buf(0);
warn("open(\"/dev/pf\")");
} else if (ioctl(pf_dev, DIOCGETSTATUS, &status)) {
warn("DIOCGETSTATUS");
alloc_buf(0);
} else
alloc_buf(status.states);
/* initialize cache with given size */
if (cache_init(cachesize))
warnx("Failed to initialize cache.");
else if (interactive && cachesize > 0)
cachestates = 1;
update_cache();
#ifdef HAVE_MAX_STATES
show_field(FLD_STMAX);
#endif
#ifdef HAVE_RULESETS
show_field(FLD_ANCHOR);
#endif
}