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
