Application layer classifier for ipfw

Ian Smith smithi at nimnet.asn.au
Sat Aug 2 12:55:11 UTC 2008


On Fri, 1 Aug 2008, Mike Makonnen wrote:
 > Patrick Tracanelli wrote:
 > > Mike Makonnen escreveu:
 > >> Hi,
 > >>
 > >> An Internet Cafe I do some work for was recently having problems with 
 > >> very slow internet access. It turns out customers were running P2P 
 > >> file sharing applications which were hogging all the bandwidth. I 
 > >> looked for  programs that would allow me to shape traffic according 
 > >> to the application layer protocol, but couldn't find any for FreeBSD. 
 > >> I found a couple: l7-filter and ipp2p, but these are Linux specific. 
 > >> So, I decided to write one. The result is ipfw-classifyd :
 > >> http://people.freebsd.org/~mtm/ipfw-classifyd.tar.bz2

This is great, Mike.  I've been 'waiting for this' for a very similar
situation for months now, getting by with dummynet bandwidth limiting
and wondering about weighted queuing, but this is a much sharper tool.

 > >> As the name implies it uses ipfw(4) to implement a userland daemon 
 > >> that classifies TCP and UDP packets according to regular expression 
 > >> patterns for various protocols. It's intended to be used with 
 > >> divert(4) sockets and dummynet(4) so you can do traffic shaping 
 > >> depending on the application level protocol. The protocol patterns 
 > >> are from the l7-filter project.

Any GPL issues with using these patterns?

 > >> Basically, you use ipfw(8) to divert tcp/udp packets to the damon. It 
 > >> reads its configuration file for a list of protocols and ipfw(8) 
 > >> rules. Then, when it detects a matching session it re-injects the 
 > >> packet back at the specified rule number. The tarball has a sample 
 > >> configuration file and firewall script to get you started.

I was confused by 'back at the specified rule number' too, especially as
you used a rule 1000 in the example, being the rule handling NON matched
packets.  So I had a browse through the code, finding: 

                /*
                 * Inform divert(4) what rule to send it to by
                 * modifying the port number of the associated sockaddr_in
                 * structure. Note: we subtract one from the ipfw(4) rule
                 * number because processing in ipfw(4) will start with
                 * the next rule *after* the supplied rule number.
                 */
                if (flow->if_fwrule != 0) {
                        pkt->fp_saddr.sin_port = flow->if_fwrule;
                        goto enqueue;
                }

and noticed that we weren't subtracting one ..

I don't believe it's quite correct to say 'ipfw will start with the next
rule after the supplied rule number'.  If there were (legitimately) 
multiple rules having the same number (either divert rules themselves or
at the target rule), the ipfw divert-return code skips past duplicates
to the next rule that has a higher rule number, which may not amount to
the same thing. 

(Sorry, Julian made me study ipfw execution behaviour months ago :)

I thought at first that this behaviour is fine, and just needed a bit
better describing.  But I'm starting to wonder if subtracting one isn't
really a better idea?

 > >> While I have not done extensive testing, preliminary tests are 
 > >> encouraging and it seems to work, so I thought I'd announce it to the 
 > >> rest of the world in case anyone else is interested in this kind of 
 > >> application.
 > >>
 > >> Comments and suggestions highly appreciated.
 > >>
 > >> Cheers.
 > >
 > > Wont compile on RELENG_6 but is working perfectly on REL_7. I am 
 > > trying hard with ssh, soulseek and msn. Its working like a charm with 
 > > the suggested rc.firewall.
 > Can you email me the compile error?

I'd like to run it on a 4.8 filtering bridge .. ah, never mind :)

 > > I have configured ipfw-classfyd.conf changing the rules, for a number 
 > > of L7 patterns, and now I try to understand why the "diverted" rules 
 > > only match if the rule number is 1 after the configured, ie, I put 
 > > soulseek to 65530 and a rule wont match there, but the very same rule 
 > > matches 65531. I will read the code, but it seems that reinjection of 
 > > the packet is made +1, correct?
 > >
 > The application doesn't do that, it's the firewall that does that. 
 > Basically, when
 > ipfw(4) diverts a packet to the application it includes the rule
 > that caused the packet to be diverted (so that when it gets it back it knows
 > where to continue processing from). When it gets the packet back it 
 > continues
 > processing the packet at the rule *after* the one that caused it to be 
 > diverted

Rather, 'at the first rule having a higher rule number than the one ..'

 > (otherwise the packet would get diverted in an endless loop). In the sample
 > script rule 1000 is the rule that passes the packets that do not get 
 > diverted, so
 > I configured ipfw-classifyd to modify the information that comes with the
 > packet to point to rule 1000 (in classifyd.conf).  So, when ipfw(4) gets the
 > packet back it continues processing it at the next rule after 1000, which is
 > the rule that sends all diverted packets through the pipe.

Yeah figuring out how rule 1000 had anything to do with it confused me
at first, when any number after the divert rule/s would do.  I think it
may need stating really explicitly, something perhaps better put than:

  The rule number configured for the specified traffic type is that of
  the last rule number *before* the target ipfw rule for a match.

Which is still harder to describe (or get one's head around) than being
able to specify the desired target rule number in the config file?

As long as you don't subtract one for the non-match packets reinjected
normally, and do subtract one from the matching packet's config rule,
so directly skipping to the specified rule, it would need an awful lot
less explaining to users ..

 > Hope that helps.

Oh yes :)  Might even have to upgrade that bridge at long last.

cheers, Ian



More information about the freebsd-net mailing list