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

Reply via email to