Hello tech@ and PF hackers especially, again.
Here is my second, more advanced try on implementing packet inspection in
PF. This time it can inspect and drop established connection. I used one
of the "pad" bytes in pf_state structure for this; hope this is harmless.
Also, there are many fixes mentioned for previous version, especially from
Bret Lambert (thanks again!). Hope I got panic logic right this time.
Non-obvious interventions has comments in the code (and, therefore, in
the patch).
The feature I thought about but did not implemented is inspecting _every_
packet in the state. If anyone will vote for it, I'll implement it, it's
rather easy now, but the diff is already long enough (almost 800 lines).
And the last I can say: It Works For Me (TM).
--
Best wishes,
Vadim Zhukov
Index: share/man/man5/pf.conf.5
===================================================================
RCS file: /cvs/src/share/man/man5/pf.conf.5,v
retrieving revision 1.476
diff -u -r1.476 pf.conf.5
--- share/man/man5/pf.conf.5 19 May 2010 13:51:37 -0000 1.476
+++ share/man/man5/pf.conf.5 20 Jun 2010 22:50:06 -0000
@@ -546,6 +546,62 @@
.Pc
must match.
.Pp
+.It Xo
+.Ar inspect Aq Ar value
+.Aq Ar op
+.Aq Ar mask
+.Aq Ar direction
+.Ar in Aq Ar pktnum
+.Ar at Aq Ar offset
+.Xc
+Tests contents of
+.Ar pktnum
+packet flowing in
+.Ar direction
+at the
+.Ar offset
+to be equal to
+.Ar value ,
+optionally applying
+.Ar mask
+operation
+.Ar op .
+Packets are numbered starting from 1, which is default.
+Packets numbers larger than 1 are meaningful only in stateful rules, and
+therefore are rejected on stateless ones.
+.Pp
+Offset starts after protocol header, numbering from 0, which is default.
+Negative offset cannot be specified.
+.Pp
+.Ar direction
+can be one of the following:
+.Bl -tag -width both_directions
+.It Ar incoming
+Only incoming packets are accounted and inspected.
+This is the default.
+.It Ar outgoing
+Only outgoung packets are accounted and inspected.
+.It Ar both directions
+Packets flowing in both directions are taken in account.
+Inspection happens once:
+.Ar pktnum
+is compared with sum of numbers of packets in and out.
+.El
+.Pp
+.Ar value
+and
+.Ar mask
+can be specified as plain strings, or as hexadecimal raw strings (i.e.,
+starting with "0x").
+In the latter case you can embed any special characters.
+Maximum length of encoded either way data is 64 bytes.
+.Pp
+Two mask operations supported are "logical and" and "logical exclusive or".
+They're specified using "&" and "^" characters, respectively.
+If no mask operation specified then packet contents is compared with
+.Ar value
+as is.
+.Pp
.It Ar label Aq Ar string
Adds a label to the rule, which can be used to identify the rule.
For instance,
@@ -2643,7 +2699,8 @@
"nat-to" ( redirhost | "{" redirhost-list "}" )
[ portspec ] [ pooltype ] [ "static-port" ] |
[ "fastroute" | route ] |
- [ "received-on" ( interface-name | interface-group ) ]
+ [ "received-on" ( interface-name | interface-group ) ] |
+ "inspect" inspect
scrubopts = scrubopt [ [ "," ] scrubopts ]
scrubopt = "no-df" | "min-ttl" number | "max-mss" number |
@@ -2786,6 +2843,10 @@
upperlimit-sc = "upperlimit" sc-spec
sc-spec = ( bandwidth-spec |
"(" bandwidth-spec number bandwidth-spec ")" )
+inspect = string [ inspect-op ] [ inspect-dir ] [ "in" number ]
+ [ "at" number ]
+inspect-op = ( "&" | "^" ) string
+inspect-dir = ( "incoming" | "outgoing" | "both" "directions" )
include = "include" filename
.Ed
.Sh FILES
Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.691
diff -u -r1.691 pf.c
--- sys/net/pf.c 7 May 2010 13:33:16 -0000 1.691
+++ sys/net/pf.c 20 Jun 2010 22:50:06 -0000
@@ -230,6 +230,10 @@
struct pf_state_key_cmp *, u_int, struct mbuf *);
int pf_src_connlimit(struct pf_state **);
int pf_check_congestion(struct ifqueue *);
+int pf_inspect(struct pf_pdesc *, struct mbuf *,
+ struct pf_rule *);
+static __inline int pf_inspect_state(struct pf_pdesc *, struct mbuf *,
+ struct pf_state *, struct pfi_kif *);
int pf_match_rcvif(struct mbuf *, struct pf_rule *);
extern struct pool pfr_ktable_pl;
@@ -246,11 +250,14 @@
enum { PF_ICMP_MULTI_NONE, PF_ICMP_MULTI_SOLICITED, PF_ICMP_MULTI_LINK };
-#define STATE_LOOKUP(i, k, d, s, m) \
+#define STATE_LOOKUP(i, k, d, s, m, pd)
\
do { \
s = pf_find_state(i, k, d, m); \
if (s == NULL || (s)->timeout == PFTM_PURGE) \
return (PF_DROP); \
+ if (!(s)->inspected && \
+ pf_inspect_state((pd), (m), (s), (i)) == PF_DROP) \
+ return (PF_DROP); \
if (d == PF_OUT && \
(((s)->rule.ptr->rt == PF_ROUTETO && \
(s)->rule.ptr->direction == PF_OUT) || \
@@ -2271,6 +2278,94 @@
}
int
+pf_inspect(struct pf_pdesc *pd, struct mbuf *m, struct pf_rule *r)
+{
+ int i, off;
+ char cv;
+
+ if (r->inspect.offset + r->inspect.len > pd->p_len)
+ return (0);
+ off = r->inspect.offset + (pd->tot_len - pd->p_len);
+ for (i = 0; i < r->inspect.len; i++, off++) {
+ m = m_getptr(m, off, &off);
+ if (m == NULL || off == m->m_len) {
+ DPFPRINTF(LOG_ERR, "pf_inspect: pd->tot_len or "
+ "pd->p_len is invalid");
+ return (0);
+ }
+ cv = m->m_data[off];
+ switch (r->inspect.op) {
+ case PF_INSOP_CMP:
+ break;
+
+ case PF_INSOP_AND:
+ cv &= r->inspect.mask[i];
+ break;
+
+ case PF_INSOP_XOR:
+ cv ^= r->inspect.mask[i];
+ break;
+
+ default:
+#ifdef DIAGNOSTIC
+ panic("pf_inspect: r->inspect.op=%d",
+ r->inspect.op);
+#endif
+ DPFPRINTF(LOG_ERR, "pf_inspect: r->inspect.op=%d",
+ r->inspect.op);
+ return (0);
+ }
+ if (cv != r->inspect.what[i])
+ return (0);
+ }
+ return (1);
+}
+
+static __inline int
+pf_inspect_state(struct pf_pdesc *pd, struct mbuf *m, struct pf_state *state,
+ struct pfi_kif *kif)
+{
+ struct pf_rule *r;
+ struct tcphdr *th;
+ u_int64_t pktnum;
+
+ r = state->rule.ptr;
+ if (!(pd->dir & r->inspect.directions))
+ return (PF_PASS);
+
+ /* Packet numbers were not bumped yet, so add 1 for current one */
+ pktnum = 1;
+ /* XXX: Are direction IDs below always correct? */
+ if (r->inspect.directions & PF_IN)
+ pktnum += state->packets[PF_SK_WIRE];
+ if (r->inspect.directions & PF_OUT)
+ pktnum += state->packets[PF_SK_STACK];
+
+ if (pktnum >= r->inspect.pktnum)
+ state->inspected = 1;
+ if (pktnum != r->inspect.pktnum)
+ return (PF_PASS);
+
+ if (!pf_inspect(pd, m, state->rule.ptr)) {
+ switch (pd->proto) {
+ case IPPROTO_TCP:
+ th = pd->hdr.tcp;
+ pf_send_tcp(state->rule.ptr, pd->af,
+ pd->dst, pd->src, th->th_dport,
+ th->th_sport, ntohl(th->th_ack), 0,
+ TH_RST, 0, 0,
+ state->rule.ptr->return_ttl, 1, 0,
+ pd->rdomain, pd->eh, kif->pfik_ifp);
+ break;
+ }
+ state->timeout = PFTM_PURGE;
+ state->src.state = state->dst.state = TCPS_CLOSED;
+ return (PF_DROP);
+ }
+ return (PF_PASS);
+}
+
+int
pf_match_rcvif(struct mbuf *m, struct pf_rule *r)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
@@ -2879,6 +2974,9 @@
r = TAILQ_NEXT(r, entries);
else if (r->match_tag && !pf_match_tag(m, r, &tag))
r = TAILQ_NEXT(r, entries);
+ else if (r->inspect.len > 0 && r->inspect.pktnum == 1 &&
+ !pf_inspect(pd, m, r))
+ r = TAILQ_NEXT(r, entries);
else if (r->rcv_kif && !pf_match_rcvif(m, r))
r = TAILQ_NEXT(r, entries);
else if (r->os_fingerprint != PF_OSFP_ANY &&
@@ -3116,6 +3214,8 @@
s->state_flags |= PFSTATE_SLOPPY;
if (r->rule_flag & PFRULE_PFLOW)
s->state_flags |= PFSTATE_PFLOW;
+ if (r->inspect.len == 0 || r->inspect.pktnum == 1)
+ s->inspected = 1;
s->log = act->log & PF_LOG_ALL;
s->qid = act->qid;
s->pqid = act->pqid;
@@ -3871,7 +3971,7 @@
key.port[0] = th->th_dport;
}
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
if (direction == (*state)->direction) {
src = &(*state)->src;
@@ -4043,7 +4143,7 @@
key.port[0] = uh->uh_dport;
}
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
if (direction == (*state)->direction) {
src = &(*state)->src;
@@ -4129,7 +4229,7 @@
PF_ACPY(&key->addr[pd->sidx], pd->src, key->af);
PF_ACPY(&key->addr[pd->didx], pd->dst, key->af);
- STATE_LOOKUP(kif, key, direction, *state, m);
+ STATE_LOOKUP(kif, key, direction, *state, m, pd);
/* Is this ICMP message flowing in right direction? */
if ((*state)->rule.ptr->type &&
@@ -4398,7 +4498,7 @@
key.port[pd2.sidx] = th.th_sport;
key.port[pd2.didx] = th.th_dport;
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
if (direction == (*state)->direction) {
src = &(*state)->dst;
@@ -4527,7 +4627,7 @@
key.port[pd2.sidx] = uh.uh_sport;
key.port[pd2.didx] = uh.uh_dport;
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
@@ -4722,7 +4822,7 @@
PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
key.port[0] = key.port[1] = 0;
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
/* translate source/destination address, if necessary */
if ((*state)->key[PF_SK_WIRE] !=
@@ -4796,7 +4896,7 @@
key.port[1] = key.port[0] = 0;
}
- STATE_LOOKUP(kif, &key, direction, *state, m);
+ STATE_LOOKUP(kif, &key, direction, *state, m, pd);
if (direction == (*state)->direction) {
src = &(*state)->src;
@@ -5602,6 +5702,9 @@
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
m, off, h, &pd, &a, &ruleset, &ipintrq);
+ /* Do not normalize based on state being unlinked */
+ else
+ break;
if (s) {
if (s->max_mss)
@@ -5629,6 +5732,7 @@
REASON_SET(&reason, PFRES_SHORT);
goto done;
}
+ pd.p_len = pd.tot_len - off - sizeof(struct udphdr);
pd.sport = &uh.uh_sport;
pd.dport = &uh.uh_dport;
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
@@ -5693,6 +5797,9 @@
}
done:
+ if (s && action == PF_DROP)
+ goto state_dropped;
+
if (action == PF_PASS && h->ip_hl > 5 &&
!((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
action = PF_DROP;
@@ -5760,6 +5867,7 @@
action = PF_DIVERT;
}
+state_dropped:
if (pflog) {
struct pf_rule_item *ri;
@@ -6026,6 +6134,9 @@
} else if (s == NULL)
action = pf_test_rule(&r, &s, dir, kif,
m, off, h, &pd, &a, &ruleset, &ip6intrq);
+ /* Do not normalize based on state being unlinked */
+ else
+ break;
if (s) {
if (s->max_mss)
@@ -6053,6 +6164,7 @@
REASON_SET(&reason, PFRES_SHORT);
goto done;
}
+ pd.p_len = pd.tot_len - off - sizeof(struct udphdr);
pd.sport = &uh.uh_sport;
pd.dport = &uh.uh_dport;
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
@@ -6145,6 +6257,9 @@
n = NULL;
}
+ if (s && action == PF_DROP)
+ goto state_dropped;
+
/* handle dangerous IPv6 extension headers. */
if (action == PF_PASS && rh_cnt &&
!((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
@@ -6203,6 +6318,7 @@
action = PF_DIVERT;
}
+state_dropped:
if (pflog) {
struct pf_rule_item *ri;
Index: sys/net/pf_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.232
diff -u -r1.232 pf_ioctl.c
--- sys/net/pf_ioctl.c 18 Jan 2010 23:52:46 -0000 1.232
+++ sys/net/pf_ioctl.c 20 Jun 2010 22:50:07 -0000
@@ -1121,6 +1121,20 @@
PFR_TFLAG_ACTIVE;
}
+ if (rule->inspect.len > 0) {
+ if (rule->inspect.len > PF_INSPECT_SIZE)
+ error = EINVAL;
+ if (rule->inspect.op >= PF_INSOP_COUNT)
+ error = EINVAL;
+ if ((rule->inspect.directions & (PF_IN | PF_OUT)) == 0)
+ error = EINVAL;
+ /* Actually, harmless but stupid */
+ if (rule->inspect.directions != PF_IN &&
+ !rule->keep_state) {
+ error = EINVAL;
+ }
+ }
+
if (error) {
pf_rm_rule(NULL, rule);
break;
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.309
diff -u -r1.309 pfvar.h
--- sys/net/pfvar.h 7 May 2010 13:33:16 -0000 1.309
+++ sys/net/pfvar.h 20 Jun 2010 22:50:07 -0000
@@ -646,6 +646,22 @@
struct pf_addr addr;
u_int16_t port;
} divert, divert_packet;
+
+ struct {
+#define PF_INSPECT_SIZE 64
+ char what[PF_INSPECT_SIZE];
+ char mask[PF_INSPECT_SIZE];
+ u_int64_t pktnum;
+ u_int32_t offset;
+#define PF_INSOP_CMP 0
+#define PF_INSOP_AND 1
+#define PF_INSOP_XOR 2
+#define PF_INSOP_COUNT 3
+ u_int16_t op;
+ u_int8_t len;
+ /* PF_IN and/or PF_OUT */
+ u_int8_t directions;
+ } inspect;
};
/* rule flags */
@@ -790,7 +806,8 @@
u_int64_t id;
u_int32_t creatorid;
u_int8_t direction;
- u_int8_t pad[3];
+ u_int8_t inspected;
+ u_int8_t pad[2];
TAILQ_ENTRY(pf_state) sync_list;
TAILQ_ENTRY(pf_state) entry_list;
cvs server: I know nothing about sys/net/pfvar.h.new
Index: sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.589
diff -u -r1.589 parse.y
--- sbin/pfctl/parse.y 23 Mar 2010 13:31:29 -0000 1.589
+++ sbin/pfctl/parse.y 20 Jun 2010 22:50:07 -0000
@@ -113,6 +113,11 @@
PFCTL_STATE_FILTER
};
+struct rawstring {
+ char *s;
+ size_t len;
+};
+
struct node_proto {
u_int8_t proto;
struct node_proto *next;
@@ -229,6 +234,16 @@
int binat;
};
+struct inspect_opts {
+ char what[PF_INSPECT_SIZE];
+ char mask[PF_INSPECT_SIZE];
+ int64_t pktnum;
+ u_int32_t offset;
+ u_int16_t op;
+ u_int8_t len;
+ u_int8_t directions;
+};
+
struct filter_opts {
int marker;
#define FOM_FLAGS 0x0001
@@ -287,6 +302,8 @@
sa_family_t af;
struct pf_poolhashkey *key;
} route;
+
+ struct inspect_opts inspect;
} filter_opts;
struct antispoof_opts {
@@ -436,6 +453,8 @@
struct table_opts table_opts;
struct pool_opts pool_opts;
struct node_hfsc_opts hfsc_opts;
+ struct rawstring rawstring;
+ struct inspect_opts inspect_opts;
} v;
int lineno;
} YYSTYPE;
@@ -467,6 +486,7 @@
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
%token DIVERTTO DIVERTREPLY DIVERTPACKET NATTO RDRTO RECEIVEDON NE LE GE
+%token INSPECT AT BOTH DIRECTIONS INCOMING OUTGOING
%token <v.string> STRING
%token <v.number> NUMBER
%token <v.i> PORTBINARY
@@ -515,6 +535,9 @@
%type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l
%type <v.table_opts> table_opts table_opt table_opts_l
%type <v.pool_opts> pool_opts pool_opt pool_opts_l
+%type <v.inspect_opts> inspect_op inspect_where
+%type <v.number> inspect_dirs inspect_in inspect_at
+%type <v.rawstring> rawstring
%%
ruleset : /* empty */
@@ -739,6 +762,39 @@
| STRING
;
+rawstring : STRING {
+ int i;
+ char cc[3];
+
+ if (strncmp($1, "0x", 2) == 0) {
+ $$.len = strlen($1) - 2;
+ if ($$.len % 2) {
+ yyerror("invalid hex string");
+ YYERROR;
+ }
+ $$.len /= 2;
+ $$.s = calloc($$.len, sizeof($$.s[0]));
+ if ($$.s == NULL)
+ err(1, "rawstring: calloc");
+ cc[2] = '\0';
+ for (i = 0; i < $$.len; i++) {
+ cc[0] = $1[2 + (i * 2)];
+ cc[1] = $1[2 + (i * 2) + 1];
+ if (!isxdigit(cc[0]) ||
+ !isxdigit(cc[1])) {
+ yyerror("invalid hex string");
+ YYERROR;
+ }
+ sscanf(cc, "%hhx", $$.s + i);
+ }
+ free($1);
+ } else {
+ $$.len = strlen($1);
+ $$.s = $1;
+ }
+ }
+ ;
+
varset : STRING '=' varstring {
if (pf->opts & PF_OPT_VERBOSE)
printf("%s = \"%s\"\n", $1, $3);
@@ -904,6 +960,19 @@
}
r.match_tag_not = $9.match_tag_not;
+ if ($9.inspect.len > 0) {
+ memcpy(r.inspect.what, $9.inspect.what,
+ $9.inspect.len);
+ if ($9.inspect.op != PF_INSOP_CMP)
+ memcpy(r.inspect.mask, $9.inspect.mask,
+ $9.inspect.len);
+ r.inspect.pktnum = $9.inspect.pktnum;
+ r.inspect.offset = $9.inspect.offset;
+ r.inspect.len = $9.inspect.len;
+ r.inspect.op = $9.inspect.op;
+ r.inspect.directions = $9.inspect.directions;
+ }
+
decide_address_family($8.src.host, &r.af);
decide_address_family($8.dst.host, &r.af);
@@ -2094,6 +2163,19 @@
}
r.divert_packet.port = $8.divert_packet.port;
+ if ($8.inspect.len > 0) {
+ memcpy(r.inspect.what, $8.inspect.what,
+ $8.inspect.len);
+ if ($8.inspect.op != PF_INSOP_CMP)
+ memcpy(r.inspect.mask, $8.inspect.mask,
+ $8.inspect.len);
+ r.inspect.pktnum = $8.inspect.pktnum;
+ r.inspect.offset = $8.inspect.offset;
+ r.inspect.len = $8.inspect.len;
+ r.inspect.op = $8.inspect.op;
+ r.inspect.directions = $8.inspect.directions;
+ }
+
expand_rule(&r, 0, $4, &$8.nat, &$8.rdr, &$8.rroute, $6,
$7.src_os,
$7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
@@ -2314,6 +2396,97 @@
}
filter_opts.rcv = $2;
}
+ | INSPECT rawstring inspect_op inspect_where {
+ if ($2.len == 0) {
+ yyerror("inspect string is empty");
+ YYERROR;
+ } else if ($2.len > PF_INSPECT_SIZE) {
+ yyerror("inspect string is longer that %d
bytes",
+ PF_INSPECT_SIZE);
+ YYERROR;
+ }
+ memcpy(filter_opts.inspect.what, $2.s, $2.len);
+ filter_opts.inspect.len = $2.len;
+
+ if ($3.len > 0) {
+ if ($3.len != $2.len) {
+ yyerror("inspect string and mask have "
+ "different length");
+ YYERROR;
+ }
+ memcpy(filter_opts.inspect.mask, $3.mask,
$3.len);
+ }
+ filter_opts.inspect.op = $3.op;
+
+ filter_opts.inspect.pktnum = $4.pktnum;
+ filter_opts.inspect.offset = $4.offset;
+ filter_opts.inspect.directions = $4.directions;
+ }
+ ;
+
+inspect_op : /* empty */ {
+ $$.op = PF_INSOP_CMP;
+ $$.len = 0;
+ }
+ | '&' rawstring {
+ if ($2.len == 0) {
+ yyerror("inspect mask string is empty");
+ YYERROR;
+ } else if ($2.len > PF_INSPECT_SIZE) {
+ yyerror("inspect mask is longer that %d bytes",
+ PF_INSPECT_SIZE);
+ YYERROR;
+ }
+ memcpy($$.mask, $2.s, $2.len);
+ $$.len = $2.len;
+ $$.op = PF_INSOP_AND;
+ }
+ | '^' rawstring {
+ if ($2.len == 0) {
+ yyerror("inspect mask string is empty");
+ YYERROR;
+ } else if ($2.len > PF_INSPECT_SIZE) {
+ yyerror("inspect mask is longer that %d bytes",
+ PF_INSPECT_SIZE);
+ YYERROR;
+ }
+ memcpy($$.mask, $2.s, $2.len);
+ $$.len = $2.len;
+ $$.op = PF_INSOP_XOR;
+ }
+ ;
+
+inspect_where : inspect_dirs inspect_in inspect_at {
+ $$.directions = $1;
+ $$.pktnum = $2;
+ $$.offset = $3;
+ }
+ ;
+
+inspect_dirs : /* empty */ { $$ = PF_IN; }
+ | INCOMING { $$ = PF_IN; }
+ | OUTGOING { $$ = PF_OUT; }
+ | BOTH DIRECTIONS { $$ = PF_IN | PF_OUT; }
+ ;
+
+inspect_in : /* empty */ { $$ = 1; }
+ | IN NUMBER {
+ if ($2 <= 0) {
+ yyerror("invalid packet number: %d", $2);
+ YYERROR;
+ }
+ $$ = $2;
+ }
+ ;
+
+inspect_at : /* empty */ { $$ = 0; }
+ | AT NUMBER {
+ if ($2 < 0) {
+ yyerror("invalid packet offset: %d", $2);
+ YYERROR;
+ }
+ $$ = $2;
+ }
;
probability : STRING {
@@ -5011,15 +5184,18 @@
{ "anchor", ANCHOR},
{ "antispoof", ANTISPOOF},
{ "any", ANY},
+ { "at", AT},
{ "bandwidth", BANDWIDTH},
{ "binat-to", BINATTO},
{ "bitmask", BITMASK},
{ "block", BLOCK},
{ "block-policy", BLOCKPOLICY},
+ { "both", BOTH},
{ "cbq", CBQ},
{ "code", CODE},
{ "crop", FRAGCROP},
{ "debug", DEBUG},
+ { "directions", DIRECTIONS},
{ "divert-packet", DIVERTPACKET},
{ "divert-reply", DIVERTREPLY},
{ "divert-to", DIVERTTO},
@@ -5044,8 +5220,10 @@
{ "if-bound", IFBOUND},
{ "in", IN},
{ "include", INCLUDE},
+ { "incoming", INCOMING},
{ "inet", INET},
{ "inet6", INET6},
+ { "inspect", INSPECT},
{ "keep", KEEP},
{ "label", LABEL},
{ "limit", LIMIT},
@@ -5073,6 +5251,7 @@
{ "optimization", OPTIMIZATION},
{ "os", OS},
{ "out", OUT},
+ { "outgoing", OUTGOING},
{ "overload", OVERLOAD},
{ "pass", PASS},
{ "pflow", PFLOW},
Index: sbin/pfctl/pfctl_parser.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.265
diff -u -r1.265 pfctl_parser.c
--- sbin/pfctl/pfctl_parser.c 16 May 2010 12:23:30 -0000 1.265
+++ sbin/pfctl/pfctl_parser.c 20 Jun 2010 22:50:07 -0000
@@ -1055,6 +1055,44 @@
print_pool(&r->route, 0, 0, r->af, PF_PASS, verbose);
}
}
+ if (r->inspect.len) {
+ printf(" inspect 0x");
+ for (i = 0; i < r->inspect.len; i++)
+ printf("%02hhx", r->inspect.what[i]);
+ if (r->inspect.op != PF_INSOP_CMP) {
+ switch (r->inspect.op) {
+ case PF_INSOP_AND:
+ printf(" & 0x");
+ break;
+ case PF_INSOP_XOR:
+ printf(" ^ 0x");
+ break;
+ default:
+ errx(1, "\nUnknown inspect operation %d",
+ r->inspect.op);
+ }
+ for (i = 0; i < r->inspect.len; i++)
+ printf("%02hhx", r->inspect.mask[i]);
+ }
+ switch ((r->inspect.directions & (PF_IN | PF_OUT))) {
+ case PF_IN:
+ printf(" incoming");
+ break;
+
+ case PF_OUT:
+ printf(" outgoing");
+ break;
+
+ case (PF_IN|PF_OUT):
+ printf(" both directions");
+ break;
+
+ default:
+ errx(1, "\nUnspecified inspect direction");
+ }
+ printf(" in %llu at %u", (unsigned long long)r->inspect.pktnum,
+ (unsigned)r->inspect.offset);
+ }
}
void