No, no, it's me who is excluding this way. :) Moving packets through
userland and reimplementing states in the app is not the simpliest,
most reliable and - last but not least - fastest way, IMHO. Please
prove me if I'm wrong.

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


-- 
--
  WBR,
  Vadim Zhukov

Reply via email to