Hm-m-m, could you explain better, please? I don't see the way to do
such filtering with diverting, excluding writing a proxy app listening
all the traffic.

2010/6/13, Bret S. Lambert <bret.lamb...@gmail.com>:
> Is there some reason that divert sockets (``man divert'') can't do
> this for you?
>
> On Sun, Jun 13, 2010 at 03:27:57AM +0400, Vadim Jukov wrote:
>> Hello, tech@, especially PF hackers!
>>
>> This is a work-in-progress patch that implements direct packet inspection
>> in PF. This is needed in the cases when traffic could not be easily
>> detected by other mechanisms. The actual example is new UDP-based
>> protocol of uTorrent program that spams networks heavily, and could be
>> detected only by comparing value at the offset 0x40 with 0x7FFFFFFFAB.
>>
>> The main reason I publish uncompleted work is that I want to receive some
>> clues, particularily:
>>
>>  1. I detect beginning of actual data as "pd->tot_len - pd->p_len" - is
>> it right (method to do so)?
>>
>>  2. I use "m_data", "m_len" and "m_next" to loop through mbuf chain - is
>> it right (method to do so)?
>>
>> Currently, it compiles, runs, but doesn't work - please do not actually
>> run this patch unless you want to duplicate my work. :)
>>
>> Thanks in advance.
>>
>> --
>>   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 12 Jun 2010 23:16:15 -0000
>> @@ -434,6 +434,33 @@
>>  rule that is used when a packet does not match any rules does not
>>  allow IP options.
>>  .Pp
>> +.It Xo
>> +.Ar inspect Aq Ar value
>> +.Ar at Aq Ar offset
>> +.Xc
>> +Tests packet contents at the
>> +.Ar offset
>> +to be equal to
>> +.Ar value .
>> +Note that offset starts after protocol header.
>> +.Ar value
>> +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
>> +.Ar value is 64 characters.
>> +.Pp
>> +.It Xo
>> +.Ar inspect Aq Ar mask
>> +.Ar maskop Aq Ar value
>> +.Ar at Aq Ar offset
>> +.Xc
>> +Same as previous, but also allows to specify a mask to applied to
>> +the data from packet before comparing to
>> +.Ar value .
>> +Two operations supported are "logical and" and "logical exclusive or".
>> +They're specified using "&" and "^" characters, respectively.
>> +.Pp
>>  .It Ar divert-packet Aq Ar port
>>  Used to send matching packets to
>>  .Xr divert 4
>> @@ -2643,7 +2670,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
>>
>>  scrubopts      = scrubopt [ [ "," ] scrubopts ]
>>  scrubopt       = "no-df" | "min-ttl" number | "max-mss" number |
>> @@ -2786,6 +2814,8 @@
>>  upperlimit-sc  = "upperlimit" sc-spec
>>  sc-spec        = ( bandwidth-spec |
>>                   "(" bandwidth-spec number bandwidth-spec ")" )
>> +inspect        = "inspect" [ inspect-op ] string "at" number
>> +inspect-op     = string ( "&" | "^" )
>>  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     12 Jun 2010 23:16:15 -0000
>> @@ -230,6 +230,8 @@
>>                          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 *);
>>  int                  pf_match_rcvif(struct mbuf *, struct pf_rule *);
>>
>>  extern struct pool pfr_ktable_pl;
>> @@ -2271,6 +2273,54 @@
>>  }
>>
>>  int
>> +pf_inspect(struct pf_pdesc *pd, struct mbuf *m, struct pf_rule *r) {
>> +    u_int32_t       at, i, mpos, pos;
>> +    char            cv;
>> +
>> +    if (r->inspect_at + r->inspect_len > pd->p_len)
>> +            return (0);
>> +    at = r->inspect_at + (pd->tot_len - pd->p_len);
>> +
>> +    for (pos = 0; pos + m->m_len < at;) {
>> +            pos += m->m_len;
>> +            m = m->m_next;
>> +            if (m == NULL)
>> +                    /* XXX: Should not be reached */
>> +                    return (0);
>> +    }
>> +    mpos = at - pos;
>> +
>> +    for (i = 0; i < r->inspect_len; i++, mpos++) {
>> +            while (mpos >= m->m_len) {
>> +                    pos += m->m_len;
>> +                    m = m->m_next;
>> +                    if (m == NULL)
>> +                            /* XXX: Should not be reached */
>> +                            return (0);
>> +                    mpos = 0;
>> +            }
>> +            switch (r->inspect_op) {
>> +            case PF_INSOP_CMP:
>> +                    cv = m->m_data[mpos];
>> +                    break;
>> +            case PF_INSOP_AND:
>> +                    cv = m->m_data[mpos] & r->inspect_mask[i];
>> +                    break;
>> +            case PF_INSOP_XOR:
>> +                    cv = m->m_data[mpos] ^ r->inspect_mask[i];
>> +                    break;
>> +            default:
>> +                    DPFPRINTF(LOG_ERR, "pf_Inspect: r->inspect_op=%d",
>> +                        r->inspect_op);
>> +                    return (0);
>> +            }
>> +            if (cv != r->inspect_what[i])
>> +                    return (0);
>> +    }
>> +    return (1);
>> +}
>> +
>> +int
>>  pf_match_rcvif(struct mbuf *m, struct pf_rule *r)
>>  {
>>      struct ifnet *ifp = m->m_pkthdr.rcvif;
>> @@ -2878,6 +2928,8 @@
>>                  r->prob <= arc4random_uniform(UINT_MAX - 1) + 1)
>>                      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 && !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);
>> 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       12 Jun 2010 23:16:15 -0000
>> @@ -1121,6 +1121,13 @@
>>                                  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 (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  12 Jun 2010 23:16:15 -0000
>> @@ -646,6 +646,17 @@
>>              struct pf_addr          addr;
>>              u_int16_t               port;
>>      }                       divert, divert_packet;
>> +
>> +#define PF_INSPECT_SIZE              64
>> +    char                     inspect_what[PF_INSPECT_SIZE];
>> +    char                     inspect_mask[PF_INSPECT_SIZE];
>> +    u_int32_t                inspect_at;
>> +    u_int16_t                inspect_len;
>> +#define PF_INSOP_CMP                0
>> +#define PF_INSOP_AND                1
>> +#define PF_INSOP_XOR                2
>> +#define PF_INSOP_COUNT              3
>> +    u_int16_t                inspect_op;
>>  };
>>
>>  /* rule flags */
>> 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       12 Jun 2010 23:16:15 -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,14 @@
>>      int                      binat;
>>  };
>>
>> +struct inspect_opts {
>> +    char            what[PF_INSPECT_SIZE];
>> +    char            mask[PF_INSPECT_SIZE];
>> +    u_int32_t       at;
>> +    u_int32_t       len;
>> +    u_int32_t       op;
>> +} inspect_opts;
>> +
>>  struct filter_opts {
>>      int                      marker;
>>  #define FOM_FLAGS   0x0001
>> @@ -287,6 +300,8 @@
>>              sa_family_t              af;
>>              struct pf_poolhashkey   *key;
>>      }                        route;
>> +
>> +    struct inspect_opts     inspect;
>>  } filter_opts;
>>
>>  struct antispoof_opts {
>> @@ -436,6 +451,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 +484,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 AND_C XOR_C
>>  %token      <v.string>              STRING
>>  %token      <v.number>              NUMBER
>>  %token      <v.i>                   PORTBINARY
>> @@ -515,6 +533,8 @@
>>  %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
>> +%type       <v.rawstring>           rawstring
>>  %%
>>
>>  ruleset             : /* empty */
>> @@ -739,6 +759,42 @@
>>              | STRING
>>              ;
>>
>> +rawstring   : STRING {
>> +                    int     i;
>> +                    char    c;
>> +
>> +                    if (strncmp($1, "0x", 2) == 0) {
>> +                            $$.len = strlen($1) - 2;
>> +                            if ($$.len % 2) {
>> +                                    yyerror("invalid hex inspect string");
>> +                                    YYERROR;
>> +                            }
>> +                            $$.len /= 2;
>> +                            $$.s = calloc($$.len, sizeof($$.s[0]));
>> +                            if ($$.s == NULL)
>> +                                    err(1, "inspect_op: calloc");
>> +                            for (i = 0; i < $$.len; i++) {
>> +                                    c = $1[(i + 1) * 2];
>> +                                    if (!isxdigit(c)) {
>> +                                            yyerror("invalid hex inspect 
>> string");
>> +                                            YYERROR;
>> +                                    }
>> +                                    $$.s[i] = (tolower(c) - 'a') << 4;
>> +                                    c = $1[(i + 1) * 2 + 1];
>> +                                    if (!isxdigit(c)) {
>> +                                            yyerror("invalid hex inspect 
>> string");
>> +                                            YYERROR;
>> +                                    }
>> +                                    $$.s[i] |= tolower(c) - 'a';
>> +                            }
>> +                            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,17 @@
>>                              }
>>                      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_at = $9.inspect.at;
>> +                            r.inspect_len = $9.inspect.len;
>> +                            r.inspect_op = $9.inspect.op;
>> +                    }
>> +
>>                      decide_address_family($8.src.host, &r.af);
>>                      decide_address_family($8.dst.host, &r.af);
>>
>> @@ -2094,6 +2161,17 @@
>>                      }       
>>                      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_at = $8.inspect.at;
>> +                            r.inspect_len = $8.inspect.len;
>> +                            r.inspect_op = $8.inspect.op;
>> +                    }
>> +
>>                      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 +2392,66 @@
>>                      }
>>                      filter_opts.rcv = $2;
>>              }
>> +            | INSPECT rawstring inspect_op AT NUMBER {
>> +                    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;
>> +
>> +                    if ($5 < 0) {
>> +                            yyerror("inspect address cannot be negative");
>> +                            YYERROR;
>> +                    }
>> +                    filter_opts.inspect.at = $5;
>> +            }
>> +            ;
>> +
>> +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;
>> +            }
>>              ;
>>
>>  probability : STRING                                {
>> @@ -5011,6 +5149,7 @@
>>              { "anchor",             ANCHOR},
>>              { "antispoof",          ANTISPOOF},
>>              { "any",                ANY},
>> +            { "at",                 AT},
>>              { "bandwidth",          BANDWIDTH},
>>              { "binat-to",           BINATTO},
>>              { "bitmask",            BITMASK},
>> @@ -5046,6 +5185,7 @@
>>              { "include",            INCLUDE},
>>              { "inet",               INET},
>>              { "inet6",              INET6},
>> +            { "inspect",            INSPECT},
>>              { "keep",               KEEP},
>>              { "label",              LABEL},
>>              { "limit",              LIMIT},
>> 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        12 Jun 2010 23:16:15 -0000
>> @@ -1055,6 +1055,27 @@
>>                      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("%02x", (int)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("%02x", (int)r->inspect_mask[i]);
>> +            }
>> +            printf(" at %u", (unsigned)r->inspect_at);
>> +    }
>>  }
>>
>>  void
>>
>


-- 
--
  WBR,
  Vadim Zhukov

Reply via email to