[Snort-devel] good news and a question

Martin Roesch roesch at ...48...
Thu Jan 18 23:16:49 EST 2001


Todd Lewis wrote:
> 
> Ok, I have refitted the paengine interface to look like this:
> 
>      typedef struct _pa_packet {
>             unsigned int code;
>             struct pcap_pkthdr pkthdr;
>             unsigned char * buf;
>      } pa_packet;
> 
>      typedef struct _paengine_s {
>         char *name; /* name of the paengine, e.g., "pcap" */
>         int (*configure)(char * config); /* config file entry for engine; 0 on>
>                                           * success, other on error */
>         int (*init)(void); /* initialize driver; 0 on success */
>         pa_packet* (*get_packet)(void);
>         int (*dispose_packet)(pa_packet *);
>         int (*close)(void); /* close driver; 0 on success */
>         int (*get_devtype)(void); /* # of the device type; called after conf() */
>         int (*get_capabilities)(void); /* caps of the driver; called after conf() */
>      } paengine_s;

This looks pretty much right.  Having the pa_engine dispose the packet
might be a problem in the case of fragments and tcp streams (and other
modules that might want to keep a packet around for a while).  It'd be
best to have a way to park packets for a while (the switchyard discussed
earlier) without having to copy them around in memory.  Once again we
come back to memory management.

> snort.c:ProcessPacket() now looks like this:
> 
>      void ProcessPacket(paengine_s *pa)
>      {
>         int i;
>         pa_packet *k;
>         Packet p;
> 
>         while(1){
> 
>          k=pa->get_packet();
>          if(k->code==PA_PACKET_CODE_OK){
>          /* call the packet decoder */
>          (*grinder)(&p, p.pkth, k->buf);
> (...This middle part is the same as it ever was...)
>          ClearDumpBuf();
>           k.code = p.verdict;
>          i=pa->dispose_packet(k);
>          } else {
>              fprintf(stderr, "Packet engine returned error %i\n", k->code);
>          }
>         }
>      }
> 
> I have modified the netfilter paengine to support this interface, and
> it at least loads.  I've not gotten it actually running yet, since I'm
> running 2.2 and don't feel like rebooting into 2.4 right now, and I've
> not modified paengine_pcap.c yet, but both of those should come probably
> tomorrow.
> 
> Marty, I know that this isn't exactly how you wanted this done, but
> the processing needed to be hoisted from the paengine somehow, and I
> didn't have any answers to my last set of questions about this matter,
> so this is the only way I knew how to do it.  If someone can get me
> answers to those questions, then I'll be happy to revise this along the
> lines of what you were suggesting.

It's fine, I have to work a day job that doesn't currently involve Snort
so I can't always be as quick as I'd like, so you develop what you want
and we'll work it in as we can.

> So, now that it compiles (ship it! ship it!), I need to retrofit the
> only other use of ProcessPacket() to fit the new interface.  Interestingly
> enough, it's a real doozy: spp_defrag.c:ReassembleIP().  There seems
> to be some confusion in the code itself as to what it is:
> 
>      /*
>       * Function: Packet *ReassembleIP(frag *froot)
>       * Purpose: Generate a Packet * to pass to DecodeIP
>       * Arguments:
>       * froot - root of the tree containing the last packet of the frame
>       * Returns: Packet *
>       */
>      Tree *ReassembleIP(Tree *froot)
>      {
>      (...)
> 
> I am guessing, based on the conflict between the comment and the code,
> that this is recently modified code.  (Again, I'm working off of 1.6.3
> for all of this.)
> 
> I hadn't really thought about the fragmentation/reassembly case.  I have
> an idea for how to handle this case, and then I need some advice on how
> to follow that plan.
> 
> The idea is this: firmly separate ProcessPacket() into four pieces, like
> this:
> 
>         char * buf = get_packet();
>         Packet * p = munge_packet();
>         handle_packet(p, buf);
>         dispose_packet(buf);
> 
> spp_defrag is called within handle_packet(), I am guessing.  I would
> have spp_defrag take its IP fragment, put it through munge_packet()
> and handle_packet(), and then take the resulting verdict on the traffic
> and apply that to the original packets.  So, a trace would look like this:
> 
> p=pa->get_packet() => munge_packet(p) => handle_packet(p)                                 +=> dispose_packet(p);
>                                                         |                                  |
>                                                         +=> q = defrag(p)                  p.status=q.status <=+
>                                                                               |                                       |
>                                                                                 +=> munge_packet(q) => handle_packet(q)
> 
> Sorry for the crappy art; I hope that that wasn't too unclear.  To put
> it another way, The main loop would call:
> 
>         char * buf = get_packet();
>         Packet * p = munge_packet();
>         handle_packet(p, buf);
>         dispose_packet(buf, p.action);
> 
> Inside handle_packet(), spp_defrag would do this:
> 
>         char *dfbuf = defrag(buf);
>         Packet * q = munge_packet();
>         handle_packet(p, dfbuf);
>         buf.action = dfbuf.action;
>         return;
> 
> This way, if the fragmented packets contain an attack, it will be detected
> in the normal course, via the examination of the defrag'd traffic, and
> a verdict passed on it.  spp_defrag will convey this verdict upwards
> so that it will be applied against the original, undefrag'd packets,
> which will in this case be discarded.
> 
> Is this right?

This looks right.  The defragger needs to handle data a little
differently from the way it does right now.  I like trimming the
payloads of frag packets off and storing them in a data structure for
later reassembly.  This cuts down on the memory overhead of keeping a
bunch of full sized packets around and makes reassembly somewhat
easier.  

The stream reassembler looks like it should do something similar as
well.  I'm beginning to think we should be devising ways to separate the
application stream from the packet stream, especially for reassembled
streams.  Slapping packet fragments together to form a defragged IP
packet is one thing, doing it for TCP streams is slightly more kludgy
(note: I'm the one who suggested that it should be done that way in the
current stream reassembler).

    -Marty


--
Martin Roesch
roesch at ...48...
http://www.snort.org




More information about the Snort-devel mailing list