Hi folks,

last month I wrote to this list [1] with a suggestion on how to extend the
current firewall package (aka uci_firewall) to support IPv6 rules.  I got a bit
side tracked in between but after a lot of trying around and refactoring I've
got a working (and I think nice) solution.  I dubbed it firewall2, ie. bumped
the PKG_VERSION (the PKG_RELEASE is currently at 0b1, I call that beta1 :).

I wrote [PATCH] in the subject but didn't really attach a patch but a tarball
containing the files.  You can also get and browser the files from my Git 
repository at Gitorious [2].

A real patch didn't make much sense because I moved the code around and
refactored it a lot.  The original code felt like spaghetti code (and had some
small bugs obviously caused by that) so to truly grok what it does and glue in
my iptables wrapper I started with some splitted the monolithic uci_firewall.sh
into a bunch of separate files.  Here's a diffstat anyway:

 package/firewall/Makefile                     |   10 +-
 package/firewall/files/20-firewall            |   35 --
 package/firewall/files/bin/fw                 |   47 +++
 package/firewall/files/firewall.config        |   28 +-
 package/firewall/files/firewall.hotplug       |   18 +
 package/firewall/files/firewall.init          |   18 +-
 package/firewall/files/lib/config.sh          |   79 +++++
 package/firewall/files/lib/core.sh            |  216 ++++++++++++
 package/firewall/files/lib/core_forwarding.sh |   40 +++
 package/firewall/files/lib/core_interface.sh  |   70 ++++
 package/firewall/files/lib/core_redirect.sh   |   54 +++
 package/firewall/files/lib/core_rule.sh       |   56 +++
 package/firewall/files/lib/core_zone.sh       |   72 ++++
 package/firewall/files/lib/fw.sh              |  164 +++++++++
 package/firewall/files/lib/uci_firewall.sh    |    5 +
 package/firewall/files/uci_firewall.sh        |  457 -------------------------
 16 files changed, 855 insertions(+), 514 deletions(-)

The files in lib are installed to /lib/firewall as they are.  It would be
trivial to merge/compile them into a single file to save space, but I checked
and installed on JFFS, the split version takes up 20 kB while a merged version
needs 18.5 kB.  Not sure its worth it.

So, let me try to summarize what I changed and the further plans I have:

To start, I prefixed all functions with fw_, all global variables with FW_ and
made sure that all variables are either localized or named in a way they won't
clash with other globals, like stuff from /lib/network.  Stuff like calling a
variable interfaces and have stuff breaking at weird places really made my head
hurt.

Second, I didn't change any (IPv4) behaviour, just added things (like the IPv6
stuff).  I tried to verify this by tracing the iptable calls and make sure they
are the same in the same order.  But typos are possible, so it would be great if
somebody with a complex setup could verify this.

From the user's perspective there is only one change:  You can now specify both
IPv4 and IPv6 addresses as src_ip and dest_ip in your firewall rules and
everything works as expected, at least if ip6tables is installed (if not, the
IPv6 rules are silently skipped).  Even better, if you don't specify a src_ip or
dest_ip, the rules are applied to both rulesets.  This is based on my assumption
that if you generally want to block access to a certain port, you probably don't
want to have a gaping hole in your firewall just because you accidently enabled
IPv6.

So the new firewall should work exactly as before, just better :)

Behind the scenes there's the obvious split into separate files. 
uci_firewall.sh is only a placeholder for backwards compatibility and to cope
with opkg not removing old files when updating a package (or does it?).  The
rest can be categorized in three types of files:


config.sh -- Here I introduced a function fw_config_get_section.  I hoped to
have something like this (ie. config_get_section) moved into /lib/config because
it should be useful at other places/packages as well.  I'll explain it in detail
in a separate mail.


core*.sh -- These bundle the firewall logic formerly found in the
uci_firewall.sh.  My plan is to make the firewall pluggable (more on this
later), so the prefix "core" in /lib/firewall is reserved for the firewall
itself.

* core.sh contains more or less the public "API" and the init script is just a
very thin wrapper around the start, stop, reload and restart functions found
here.  Also, here the defaults are loaded, includes are handled, general stuff
like that.

* The files core_rule.sh, core_forwarding.sh and core_redirect.sh represent
these three types of firewall rules/sections in the config files.  These are
called in a config_foreach in core.sh.  This is also true for core_zone.sh
contains the setup of the helper tables and jumps for each zone definition.

* Finally the stuff in core_interface.sh is called when interfaces come and go. 
That code was previously split between the hotplug file (which is now only a
thin wrapper) and some functions in the core.  I think especially that code path
is now a lot easier to understand.

* I already mentioned that I plan to make this firewall pluggable.  It will work
by packages just throwing scripts/libraries into /lib/firewall; these can define
functions which are called via simple after and before hooks.  I planned hooks
for core, zone and interface.  Ie. if you'd create a file /lib/firewall/foo.sh,
the firewall looks for functions called foo_before_zone, foo_after_core, etc.
and calls these when that stuff changes.


fw.sh -- This, or more precise the function fw, is the wrapper which does all
the magic.  It has an interface of which an extended version also available via
the script fw (installed in /sbin) on the command line.  I'll first explain the
command line interface because it's a bit nicer.

I'll put an extensive documentation of these commands into the wiki and/or the
official docs if the patch itsels is accepted.

The general syntax boils down to what iptables actions are about and are always
(some parameters are optional):
  fw $command $family $table $chain $target $position { $rules }

Ie. execute $command for $family on table $table's chain $chain (at position
$position) and make it jump to $target if $rules apply.  Yes, the curly braces
are part of the syntax.  There is also an version with two blocks where this
actually makes sense.  I guess its easiest to explain by examples:

  # Enable forwarding for both IPv4 and IPv6
  fw add ip filter FORWARD ACCEPT

  # Block access to the port mapper
  fw add ip filter INPUT DENY { --dport portmap }

  # Create a chain http_ip4_server
  fw add ip filter http_ip4_server

  # Oops, we wanted to create this for IPv4 only, delete this chain
  fw del ip6 filter http_ip4_filter

  # All IPv4 access to HTTP should go thru the rule above.  The caret
  # means prepend this rule to the chain (the opposite would be the $ 
  # but appending is default anyway).
  fw add ip4 filter INPUT http_ip4_server ^ { --proto tcp --dport 80 }

  # We can also specify a position by number, this is the same rule
  # as above.
  fw add ip4 filter INPUT http_ip4_server 1 { --proto tcp --dport 80 }

  # Remove that rule again.
  fw del ip4 filter INPUT http_ip4_server { --proto tcp --dport 80 }

  # Remove the first rule.  We dont want to specify a target, so use
  # a dash.
  fw del ip4 filter INPUT - 1

  # Beware, magic:  Do what I say, but chose the correct protocol 
  # family automatically based on the IP address(es) in the first 
  # block of braces.
  ADDR=192.0.2.1 fw add ip nat PREROUTING DNAT { $ADDR } { \
    --dport 443 \
    --to-destination $ADDR \
  }

  # Adding support for arptables...
  fw add arp filter IN DROP { -z aa:bb:cc:dd:ee:ff }
  # ... and ebtables was trivial, only a line for each.
  fw add eth filter INPUT DROP { -s aa:bb:cc:dd:ee:ff }


I wrote before that the external syntax is a bit nicer than the internal one: 
The internal uses one-letter family identifiers (4, 6, i, e, a) and can't
magically detect the two-brace version, you've got to use an upper case I for
that.  I first made this for performance reasons but I thought about it and
think that I could also move the case from the script into the function,
effectively making the second parameter an option:  -4, -6, -i, -e or -a where
-i is the default and decides between -i and -I based on the existence of the
second brace block.  The current wrapper doesn't really make a difference
compared to the plain iptables yet and I doubt one case will make a measurable
difference.

Also, there is not default table as you might have noticed, you've always got to
specify filter if you edit it.  But there are abbreviations:  filter, mangle,
nat and raw can be written as f, m, n, and r on the command line, saving bytes
all over the scripts ;)

The fw command doesn't do add'ing and del'ing, it also knows the commands start,
stop, restart, reload (which call the init script) and flush (for a table),
policy (for a built-in chain) and list (always with -vn).

Also there is a command called has which just returns a result code:
  # Is ip6tables installed?
  fw has ip6
  # Are both iptables and ip6tables installed?
  fw has ip
  # Does IPv6 have a nat table?
  fw has ip6 nat

These commands are used internally to ensure that rules are added only to tables
which are supported.

This is currently used to filter out the MASQUERADING rules for IPv6:  ip6tables
doesn't have a nat table per default.  I've got to find a better, more explicit
solution for this or things will break horribly for some people.


I tested the firewall quite a bit but not with a very complex rule set.  I 
really
need somebody to do this, if you need help with installing the package, I can 
help
you (on IRC as well).  Oh, and the fw script has a nice feature:  export 
FW_TRACE=1
and it will tell you about all calls to iptables it does.

Any other comments are appreciated as well :)


Cheers,
Malte


[1]http://thread.gmane.org/gmane.comp.embedded.openwrt.devel/3387
[2]http://gitorious.org/sixwrt/packages/trees/master/package/firewall

-- 
   

Attachment: firewall.tar.gz
Description: application/compressed-tar

_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to