On Sun, Jun 13, 2010 at 12:41:01PM +0400, Vadim Zhukov wrote:
> 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.

Why do you assume I'm excluding a proxy app?

> 
> 2010/6/13, Bret S. Lambert <[email protected]>:
> > 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