On 12.12.2011 05:10, Ondrej Zajicek wrote:
On Sun, Dec 11, 2011 at 05:25:08PM +0400, Alexander V. Chernikov wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hello list!
This patch adds 'firewall' protocol permitting prefixes announced to
this protocol to be put in configured firewall table with optional value.
Nice!
I have one major comment. It is a good idea to have a generic protocol
'firewall'? From the code, it seems like an almost empty shell to me,
which just complicates design and adding fwtype-specific features and
config options. I would just use separate protocols (ipfw, pf, ipset).
Well, there are at least 3 protocols instead of one. This bumps maximum
number of protocols/protocol attributes significantly which is not good
for multiprotocol patches (and hardens config portability for filter
writers). It is also the same design bird is using for kernel proto, for
example.
Some minor comments below:
Supported firewalls: IPFW, PF, *
Optional value support: IPFW, *
Sample configuration:
protocol bgp {
..
import filter { fw_value = 42; accept; } # Set firewall optional value
for each prefix
}
protocol firewall {
fwtype ipfw;
fwtable "2";
export all;
flush always; # do flush both on startup and shutdown
};
Tested on FreeBSD 8.X, PF should work on Open/NetBSD, too.
[*] I can add support for ipset on demand. However I can't understand
how it can be [effectively] used without some kind of radix/rbtree
backend (according to docs).
Really? I also looked on ipset (but i cannot find any useful docs)
and the kernel interface seemed to be similar to pf (i.e. commands
to add/remove prefixes from a set).
I have set up debian with ipset 6.10 so I plan to add all these various
table types to bird before this year ends.
+<descrip>
+ <tag>fwtype pf|ipfw</tag> Select firewall type.
+ <tag>fwtable<m/name/</tag> Specifies firewall table name.
+ <tag>flush on startup|shutdown</tag>Perform table flush on protocol
startup or shutdown.
+ <tag>flush always</tag>Perform table flush on protocol startup and
shutdown.
Flush should be probably default, otherwise users get error messages
after BIRD restart. It would also be more consistent with Kernel
protocol behavior. Perhaps also use 'keep' keyword instead of 'flush',
like in Kernel proto?
Okay, flush by default which is reasonable, and { keep on start, keep on
shutdown, keep (for both)} ?
+
+static void
+firewall_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte
*old, ea_list *attrs)
+{
...
+ if (old&& new&& p->fw->fw_replace)
+ {
+ if (!p->fw->fw_replace(p->fwdata, n, prefix_data))
+ log(L_ERR "Replacing prefix %I/%d with data '%S' failed", n->n.prefix,
n->n.pxlen, prefix_data);
+ return;
+ }
+
+ if (old)
+ if (!p->fw->fw_del(p->fwdata, n))
+ log(L_ERR "Removing prefix %I/%d failed", n->n.prefix, n->n.pxlen);
+
+ if (new)
+ if (!p->fw->fw_add(p->fwdata, n, prefix_data))
+ log(L_ERR "Adding prefix %I/%d with data '%s' failed", n->n.prefix,
n->n.pxlen, prefix_data);
These error messages should definitely be rate limited (see log_rl()).
Also you have an error message in both the generic code and the
fwtype-specific backend.
Fixed, thanks.
+static int
+firewall_start(struct proto *P)
+{
+ struct firewall_proto *p = (struct firewall_proto *) P;
+ struct firewall_config *c = (struct firewall_config *)P->cf;
+ void *fwdata;
+
+ if ((fwdata = p->fw->fw_init(P, c->fwtable)) == NULL)
+ return PS_DOWN;
+
+ p->fwdata = fwdata;
+
+ /* Flush table if needed */
+ if ((c->flush_start)&& (p->fw->fw_flush))
+ if (!p->fw->fw_flush(fwdata))
+ {
+ log(L_ERR "flush failed for table %s", c->fwtable);
+ return PS_DOWN;
I guess that protocol start() function is not supposed
to return PS_DOWN (just PS_START or PS_UP).
Fromt the code it seems that returning PS_DOWN would cause some problems
in the state machine.
Okay. So I should hang in PS_START in this case (and periodically try to
do init, as this is done in l3vpn) ?
+static void
+firewall_get_status(struct proto *P, byte *buf)
+{
+ struct firewall_config *c = (struct firewall_config *) P->cf;
+
+ bsprintf(buf, "Table [%s]", c ? c->fwtable : "none");
This is unnecessary, c should be always non-NULL.
+void
+ipfw_fw_shutdown(void *_priv)
+{
+ struct ipfw_priv *priv = _priv;
+
+ if (--ipfw_instance_count == 0)
+ {
+ DBG("Closing ipfw socket %d\n", ipfw_fd);
+ close(ipfw_fd);
+ ipfw_fd = -1;
+ }
+
+ mb_free(priv);
+}
This is unnecessary. After shutdown, everything from protocol pool is freed.
It's hard to get rid of some habits :)
Btw, moving integer attributes to unsigned helps passing IPv4 addresses
as attribute.
>From c99266ef16e66f94f22a2f78dcea82c795c4611f Mon Sep 17 00:00:00 2001
From: Alexander V. Chernikov <[email protected]>
Date: Fri, 23 Dec 2011 13:47:59 +0000
Subject: [PATCH 1/1] * Add firewall support, v2
---
configure.in | 6 +-
doc/bird.sgml | 34 ++++
nest/proto.c | 3 +
nest/protocol.h | 2 +-
nest/route.h | 3 +-
proto/firewall/Doc | 1 +
proto/firewall/Makefile | 6 +
proto/firewall/config.Y | 77 +++++++++
proto/firewall/firewall.c | 198 ++++++++++++++++++++++
proto/firewall/firewall.h | 54 ++++++
sysdep/autoconf.h.in | 5 +
sysdep/bsd/Modules | 1 +
sysdep/bsd/fw.c | 404 +++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 791 insertions(+), 3 deletions(-)
create mode 100644 proto/firewall/Doc
create mode 100644 proto/firewall/Makefile
create mode 100644 proto/firewall/config.Y
create mode 100644 proto/firewall/firewall.c
create mode 100644 proto/firewall/firewall.h
create mode 100644 sysdep/bsd/fw.c
diff --git a/configure.in b/configure.in
index 46a6ecd..bb5f445 100644
--- a/configure.in
+++ b/configure.in
@@ -47,7 +47,7 @@ if test "$enable_ipv6" = yes ; then
else
ip=ipv4
SUFFIX6=""
- all_protocols=bgp,ospf,pipe,rip,static
+ all_protocols=bgp,ospf,pipe,rip,static,firewall
fi
if test "$with_protocols" = all ; then
@@ -126,10 +126,13 @@ else
ipv4:netbsd*) sysdesc=bsd
CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
+ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
;;
ipv6:freebsd*) sysdesc=bsd-v6
;;
ipv4:freebsd*) sysdesc=bsd
+ AC_DEFINE(CONFIG_FIREWALL_IPFW, 1)
+ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
;;
ipv6:kfreebsd*) sysdesc=bsd-v6
;;
@@ -138,6 +141,7 @@ else
ipv6:openbsd*) sysdesc=bsd-v6
;;
ipv4:openbsd*) sysdesc=bsd
+ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
;;
*) AC_MSG_ERROR([Cannot determine correct system
configuration. Please use --with-sysconfig to set it manually.])
;;
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 7f53f02..3916a2c 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -2490,6 +2490,40 @@ protocol static {
}
</code>
+<sect>Firewall
+
+<p>Firewall protocol doesn't communicate with any network devices,
+but instead it allows you to add announced prefixes to given firewall table.
+At the moment IPFW and PF are supported. One can also specify special integer
tag
+that can be passed as argument to IPFW table. Any number of instances can be
configured.
+
+<p>Firewall protocol does not have many configuration options.
+
+<descrip>
+ <tag>fwtype pf|ipfw</tag> Select firewall type.
+ <tag>fwtable <m/name/</tag> Specifies firewall table name.
+ <tag>keep on startup|shutdown</tag>Do not flush table on protocol
startup or shutdown.
+ <tag>keep always</tag>Do not flush table on protocol startup and
shutdown.
+</descrip>
+
+<p>Firewall defines single route attribute:
+
+<descrip>
+ <tag>int <cf/fw_value/</tag> Value that can be passed with prefix.
+ Value is unsigned 4-byte integer. It can be set when importing routes
from the other
+ protocols or on protocol export.
+</descrip>
+
+<p>Example firewall config might look like this:
+
+<p><code>
+protocol firewall {
+ table testable; # Connect to a non-default routing
table
+ fwtype ipfw; # Use IPFW as backend
+ fwtable "2"; # Use table 2
+ export filter { fw_value = 125; accept; }; # Set value 125 for all
prefixes
+}
+</code>
<chapt>Conclusions
<sect>Future work
diff --git a/nest/proto.c b/nest/proto.c
index d55c348..85bdb19 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -632,6 +632,9 @@ protos_build(void)
#ifdef CONFIG_BGP
proto_build(&proto_bgp);
#endif
+#ifdef CONFIG_FIREWALL
+ proto_build(&proto_firewall);
+#endif
proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool);
proto_flush_event->hook = proto_flush_all;
diff --git a/nest/protocol.h b/nest/protocol.h
index a7518c2..d09a556 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -73,7 +73,7 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static,
- proto_ospf, proto_pipe, proto_bgp;
+ proto_ospf, proto_pipe, proto_bgp, proto_firewall;
/*
* Routing Protocol Instance
diff --git a/nest/route.h b/nest/route.h
index a4c0154..e5f18dd 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -349,7 +349,8 @@ typedef struct eattr {
#define EAP_RIP 2 /* RIP */
#define EAP_OSPF 3 /* OSPF */
#define EAP_KRT 4 /* Kernel route attributes */
-#define EAP_MAX 5
+#define EAP_FIREWALL 5 /* Abstact firewall interface */
+#define EAP_MAX 6
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_PROTO(ea) ((ea) >> 8)
diff --git a/proto/firewall/Doc b/proto/firewall/Doc
new file mode 100644
index 0000000..5779342
--- /dev/null
+++ b/proto/firewall/Doc
@@ -0,0 +1 @@
+S firewall.c
diff --git a/proto/firewall/Makefile b/proto/firewall/Makefile
new file mode 100644
index 0000000..a322ab6
--- /dev/null
+++ b/proto/firewall/Makefile
@@ -0,0 +1,6 @@
+source=firewall.c
+root-rel=../../
+dir-name=proto/firewall
+
+include ../../Rules
+
diff --git a/proto/firewall/config.Y b/proto/firewall/config.Y
new file mode 100644
index 0000000..aefc606
--- /dev/null
+++ b/proto/firewall/config.Y
@@ -0,0 +1,77 @@
+/*
+ * BIRD -- Firewall Protocol Configuration
+ *
+ * (c) 2011 Alexander V. Chernikov <[email protected]>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/firewall/firewall.h"
+
+CF_DEFINES
+
+#define FIREWALL_CFG ((struct firewall_config *) this_proto)
+
+CF_DECLS
+
+CF_KEYWORDS(FIREWALL, FWTABLE, FWTYPE, FW_VALUE, IPFW, PF, IPSET, KEEP, ON,
STARTUP, SHUTDOWN, ALWAYS)
+
+%type <i> firewall_type
+CF_GRAMMAR
+
+CF_ADDTO(proto, firewall_proto '}')
+
+firewall_proto_start: proto_start FIREWALL {
+ this_proto = proto_config_new(&proto_firewall, sizeof(struct
firewall_config), $1);
+ this_proto->preference = 0;
+ FIREWALL_CFG->flush_start = 1;
+ FIREWALL_CFG->flush_shutdown = 1;
+ }
+ ;
+
+firewall_proto:
+ firewall_proto_start proto_name '{'
+ | firewall_proto proto_item ';'
+ | firewall_proto firewall_proto_item ';'
+ ;
+
+firewall_proto_item:
+ FWTYPE firewall_type {
+ switch ($2)
+ {
+#ifdef CONFIG_FIREWALL_IPFW
+ case FWTYPE_IPFW:
+ break;
+#endif
+#ifdef CONFIG_FIREWALL_PF
+ case FWTYPE_PF:
+ break;
+#endif
+#ifdef CONFIG_FIREWALL_IPSET
+ case FWTYPE_IPSET:
+ break;
+#endif
+ default:
+ cf_error("firewall type is not supported by your OS/build");
+ }
+ FIREWALL_CFG->fwtype = $2;
+ };
+ | FWTABLE TEXT { FIREWALL_CFG->fwtable = $2; }
+ | KEEP ON STARTUP { FIREWALL_CFG->flush_start = 0; }
+ | KEEP ON SHUTDOWN { FIREWALL_CFG->flush_shutdown = 0; }
+ | KEEP ALWAYS { FIREWALL_CFG->flush_start = 0; FIREWALL_CFG->flush_shutdown =
0; }
+ ;
+
+firewall_type:
+ IPFW { $$ = FWTYPE_IPFW; }
+ | PF { $$ = FWTYPE_PF; }
+ | IPSET { $$ = FWTYPE_IPSET; }
+ ;
+
+CF_ADDTO(dynamic_attr, FW_VALUE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT,
EA_FIREWALL_VALUE); })
+
+CF_CODE
+
+CF_END
diff --git a/proto/firewall/firewall.c b/proto/firewall/firewall.c
new file mode 100644
index 0000000..e447470
--- /dev/null
+++ b/proto/firewall/firewall.c
@@ -0,0 +1,198 @@
+/*
+ * BIRD -- Firewall Protocol Configuration
+ *
+ * (c) 2011 Alexander V. Chernikov <[email protected]>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Firewall
+ *
+ * Firewall protocol is very simple. It adds or removes exported routes to
given firewall
+ * table with zero (or filter-specified) value. Table can be flushed on
startup to
+ * avoid error messages on bird restart.
+ */
+
+#undef LOCAL_DEBUG
+
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "lib/string.h"
+
+#include "firewall.h"
+
+static int init_done = 0;
+struct rate_limit rl_fw_err;
+
+static void
+firewall_collect(void)
+{
+ memset(&firewalls, 0, sizeof(firewalls));
+ log(L_DEBUG "Initializing firewalls..");
+#ifdef CONFIG_FIREWALL_IPFW
+ firewalls[FWTYPE_IPFW] = &fw_ipfw;
+ log(L_DEBUG "IPFW..");
+#endif
+#ifdef CONFIG_FIREWALL_PF
+ firewalls[FWTYPE_PF] = &fw_pf;
+ log(L_DEBUG "PF..");
+#endif
+#ifdef CONFIG_FIREWALL_IPSET
+ firewalls[FWTYPE_IPSET] = &fw_ipset;
+ log(L_DEBUG "IPSET..");
+#endif
+}
+
+static void
+firewall_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte
*old, ea_list *attrs)
+{
+ struct firewall_proto *p = (struct firewall_proto *) P;
+ u32 prefix_val;
+ char prefix_data[10];
+
+ if (!new && !old)
+ return;
+
+ prefix_val = ea_get_int(attrs, EA_FIREWALL_VALUE, 0);
+
+ if (prefix_val)
+ bsnprintf(prefix_data, sizeof(prefix_data), "%u", prefix_val);
+ else
+ prefix_data[0] = '\0';
+
+ DBG("Got prefix %I/%d with data '%s'\n", n->n.prefix, n->n.pxlen,
prefix_data);
+
+ if (old && new && p->fw->fw_replace)
+ {
+ p->fw->fw_replace(p->fwdata, n, prefix_data);
+ return;
+ }
+
+ if (old)
+ p->fw->fw_del(p->fwdata, n);
+
+ if (new)
+ p->fw->fw_add(p->fwdata, n, prefix_data);
+}
+
+static int
+firewall_start(struct proto *P)
+{
+ struct firewall_proto *p = (struct firewall_proto *) P;
+ struct firewall_config *c = (struct firewall_config *)P->cf;
+ void *fwdata;
+
+ if ((fwdata = p->fw->fw_init(P, c->fwtable)) == NULL)
+ return PS_START;
+
+ p->fwdata = fwdata;
+
+ /* Flush table if needed */
+ if ((c->flush_start) && (p->fw->fw_flush))
+ if (!p->fw->fw_flush(fwdata))
+ {
+ log(L_ERR "flush failed for table %s", c->fwtable);
+ return PS_START;
+ }
+
+ return PS_UP;
+}
+
+static int
+firewall_shutdown(struct proto *P)
+{
+ struct firewall_proto *p = (struct firewall_proto *) P;
+ struct firewall_config *c = (struct firewall_config *)P->cf;
+
+ log(L_DEBUG, "Shutdown requested");
+
+ /* Flush table if needed */
+ if ((c->flush_shutdown) && (p->fw->fw_flush))
+ if (!p->fw->fw_flush(p->fwdata))
+ log(L_ERR "flush failed for table %s", c->fwtable);
+
+ p->fw->fw_shutdown(p->fwdata);
+
+ return PS_DOWN;
+}
+
+static struct proto *
+firewall_init(struct proto_config *C)
+{
+ struct firewall_config *c = (struct firewall_config *) C;
+ struct proto *P = proto_new(C, sizeof(struct firewall_proto));
+ struct firewall_proto *p = (struct firewall_proto *) P;
+
+ /* Configure firewalls */
+ if (!init_done)
+ {
+ init_done = 1;
+ firewall_collect();
+ }
+
+ p->fwtype = c->fwtype;
+ p->fw = firewalls[p->fwtype];
+ P->accept_ra_types = RA_OPTIMAL;
+ P->rt_notify = firewall_rt_notify;
+
+ return P;
+}
+
+static int
+firewall_reconfigure(struct proto *P, struct proto_config *new)
+{
+ struct firewall_config *o = (struct firewall_config *) P->cf;
+ struct firewall_config *n = (struct firewall_config *) new;
+
+ if ((o->fwtype != n->fwtype) || (strcmp(o->fwtable, n->fwtable)))
+ return 0;
+
+ return 1;
+}
+
+static void
+firewall_copy_config(struct proto_config *dest, struct proto_config *src)
+{
+ /* Just a shallow copy, not many items here */
+ proto_copy_rest(dest, src, sizeof(struct firewall_config));
+}
+
+static void
+firewall_get_status(struct proto *P, byte *buf)
+{
+ struct firewall_config *c = (struct firewall_config *) P->cf;
+
+ bsprintf(buf, "Table [%s]", c->fwtable);
+}
+
+static int
+firewall_get_attr(eattr * a, byte * buf, int buflen UNUSED)
+{
+ switch (a->id)
+ {
+ case EA_FIREWALL_VALUE:
+ bsprintf(buf, "fw_value");
+ return GA_NAME;
+ default:
+ return GA_UNKNOWN;
+ }
+}
+
+
+struct protocol proto_firewall = {
+ name: "Firewall",
+ template: "fw%d",
+ attr_class: EAP_FIREWALL,
+ init: firewall_init,
+ start: firewall_start,
+ shutdown: firewall_shutdown,
+ reconfigure: firewall_reconfigure,
+ copy_config: firewall_copy_config,
+ get_status: firewall_get_status,
+ get_attr: firewall_get_attr,
+};
diff --git a/proto/firewall/firewall.h b/proto/firewall/firewall.h
new file mode 100644
index 0000000..c97ed38
--- /dev/null
+++ b/proto/firewall/firewall.h
@@ -0,0 +1,54 @@
+/*
+ * BIRD -- Firewall Protocol Configuration
+ *
+ * (c) 2011 Alexander V. Chernikov <[email protected]>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_FIREWALL_H_
+#define _BIRD_FIREWALL_H_
+
+#define FWTYPE_IPFW 0
+#define FWTYPE_PF 1
+#define FWTYPE_IPSET 2
+
+#define FWTYPE_MAX 3
+
+#define EA_FIREWALL_VALUE EA_CODE(EAP_FIREWALL, 0)
+
+struct firewall_config {
+ struct proto_config c;
+ int fwtype; /* Firewall type */
+ char *fwtable; /* Firewall table to write to */
+ int flush_start; /* Do table flush on startup? */
+ int flush_shutdown; /* Do table flush on shutdown? */
+};
+
+struct firewall_control {
+ int fwtype; /* Firewall type */
+ char *description; /* Firewall description */
+ void *(*fw_init)(struct proto *, char *); /* Init firewall instance */
+ void (*fw_shutdown)(void *); /* Shutdown firewall instance */
+ int (*fw_flush)(void *); /* Flush firewall table */
+ int (*fw_add)(void *, net *, char *); /* Add record to table */
+ int (*fw_del)(void *, net *); /* Remove record from table */
+ int (*fw_replace)(void *, net *, char *); /* Replace record. Optional */
+};
+
+struct firewall_control * firewalls[FWTYPE_MAX];
+
+struct firewall_proto {
+ struct proto p;
+ int fwtype; /* Firewall type */
+ struct firewall_control *fw; /* Pointer to configured protocol type
*/
+ void *fwdata; /* Firewall instance private
data */
+};
+
+extern struct protocol proto_firewall;
+
+extern struct firewall_control fw_ipfw, fw_pf, fw_ipset;
+extern struct rate_limit rl_fw_err;
+#define FW_ERR(x, y...) log_rl(&rl_fw_err, L_ERR x, ##y)
+
+#endif
diff --git a/sysdep/autoconf.h.in b/sysdep/autoconf.h.in
index d029e2a..c1fcdf7 100644
--- a/sysdep/autoconf.h.in
+++ b/sysdep/autoconf.h.in
@@ -42,6 +42,11 @@
#undef CONFIG_BGP
#undef CONFIG_OSPF
#undef CONFIG_PIPE
+#undef CONFIG_FIREWALL
+
+#undef CONFIG_FIREWALL_IPFW
+#undef CONFIG_FIREWALL_PF
+#undef CONFIG_FIREWALL_IPSET
/* We have <syslog.h> and syslog() */
#undef HAVE_SYSLOG
diff --git a/sysdep/bsd/Modules b/sysdep/bsd/Modules
index 84abffd..77f26e3 100644
--- a/sysdep/bsd/Modules
+++ b/sysdep/bsd/Modules
@@ -4,3 +4,4 @@ sysio.h
krt-set.h
krt-sock.c
krt-sock.h
+fw.c
diff --git a/sysdep/bsd/fw.c b/sysdep/bsd/fw.c
new file mode 100644
index 0000000..e841e06
--- /dev/null
+++ b/sysdep/bsd/fw.c
@@ -0,0 +1,404 @@
+/*
+ * BIRD -- IPFW/PF manipulations
+ *
+ * (c) 2011 Alexander V. Chernikov <[email protected]>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <err.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#undef LOCAL_DEBUG
+
+#include "nest/bird.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "lib/timer.h"
+#include "lib/unix.h"
+#include "lib/krt.h"
+#include "lib/string.h"
+#include "lib/socket.h"
+#ifdef CONFIG_FIREWALL
+#include "proto/firewall/firewall.h"
+#ifdef CONFIG_FIREWALL_IPFW
+#include "netinet/ip_fw.h"
+#endif
+#ifdef CONFIG_FIREWALL_PF
+#include "net/pfvar.h"
+#endif
+
+#ifdef CONFIG_FIREWALL_IPFW
+
+int ipfw_fd = -1;
+int ipfw_instance_count = 0;
+
+struct ipfw_priv {
+ int table; /* Table number */
+ pool *pool; /* Protocol pool */
+};
+
+int
+ipfw_do_cmd(int optname, void *optval, uintptr_t optlen)
+{
+ return setsockopt(ipfw_fd, IPPROTO_IP, optname, optval, optlen);
+}
+
+void *
+ipfw_fw_init(struct proto *p, char *table)
+{
+ pool *fwpool = p->pool;
+ int table_num = strtol(table, NULL, 10);
+ int tables_max;
+ size_t len = sizeof(tables_max);
+
+ if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, NULL, 0) ==
-1)
+ {
+ log(L_ERR "Error getting maximum ipfw table count");
+ tables_max = IPFW_TABLES_MAX;
+ }
+ DBG("ipfw maximum table count set to %d\n", tables_max);
+
+ if ((table_num < 0) || (table_num >= tables_max))
+ {
+ log(L_ERR "ipfw table %d is not within possible range (0..%d)",
table_num, tables_max);
+ return NULL;
+ }
+
+ if (ipfw_fd == -1)
+ {
+ if ((ipfw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
+ {
+ log(L_ERR "ipfw: error opering raw socket: %m");
+ return NULL;
+ }
+ DBG("Opened IPFW socked %d\n", ipfw_fd);
+ }
+
+ struct ipfw_priv *priv = mb_alloc(fwpool, sizeof(struct ipfw_priv));
+
+ priv->table = table_num;
+ priv->pool = fwpool;
+
+ ipfw_instance_count++;
+
+ return priv;
+}
+
+void
+ipfw_fw_shutdown(void *_priv UNUSED)
+{
+ if (--ipfw_instance_count == 0)
+ {
+ DBG("Closing ipfw socket %d\n", ipfw_fd);
+ close(ipfw_fd);
+ ipfw_fd = -1;
+ }
+}
+
+int
+ipfw_fw_flush(void *_priv)
+{
+ struct ipfw_priv *priv = _priv;
+ ipfw_table_entry ent;
+
+ memset(&ent, 0, sizeof(ent));
+ ent.tbl = priv->table;
+
+ log(L_DEBUG "Flushing ipfw table %d", priv->table);
+
+ if (ipfw_do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) == -1)
+ {
+ log(L_ERR "Error flushing ipfw table %d: %m", priv->table);
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ipfw_fw_add(void *_priv, net *n, char *prefixdata)
+{
+ struct ipfw_priv *priv = _priv;
+ ip_addr addr;
+ ipfw_table_entry ent;
+
+ addr = n->n.prefix;
+ ipa_hton(addr);
+
+ ent.masklen = n->n.pxlen;
+ memcpy(&ent.addr, &addr, sizeof(ip_addr));
+ ent.value = strtol(prefixdata, NULL, 0);
+ ent.tbl = priv->table;
+
+ DBG("Adding %I/%d to ipfw table %d with value %s\n", n->n.prefix,
n->n.pxlen, priv->table, prefixdata);
+
+ if (ipfw_do_cmd(IP_FW_TABLE_ADD, &ent, sizeof(ent)) == -1)
+ {
+ FW_ERR("Error adding %I/%d to ipfw table %d: %m", n->n.prefix,
n->n.pxlen, priv->table);
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ipfw_fw_del(void *_priv, net *n)
+{
+ struct ipfw_priv *priv = _priv;
+ ip_addr addr;
+ ipfw_table_entry ent;
+
+ addr = n->n.prefix;
+ ipa_hton(addr);
+
+ ent.masklen = n->n.pxlen;
+ memcpy(&ent.addr, &addr, sizeof(ip_addr));
+ ent.value = 0;
+ ent.tbl = priv->table;
+
+ DBG("Removing %I/%d from ipfw table %d\n", n->n.prefix, n->n.pxlen,
priv->table);
+
+ if (ipfw_do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent)) == -1)
+ {
+ FW_ERR("Error removing %I/%d from ipfw table %d: %m", n->n.prefix,
n->n.pxlen, priv->table);
+ return 0;
+ }
+
+ return 1;
+}
+
+struct firewall_control fw_ipfw = {
+ fwtype: FWTYPE_IPFW,
+ description: "IPFW",
+ fw_init: ipfw_fw_init,
+ fw_shutdown: ipfw_fw_shutdown,
+ fw_flush: ipfw_fw_flush,
+ fw_add: ipfw_fw_add,
+ fw_del: ipfw_fw_del,
+};
+#endif
+
+#ifdef CONFIG_FIREWALL_PF
+
+#define PF_DEVNAME "/dev/pf"
+int pf_fd = -1;
+int pf_instance_count = 0;
+
+struct pf_priv {
+ struct pfr_table table; /* PF table structure */
+ pool *pool; /* Protocol pool */
+};
+
+#define pf_tablename table.pfrt_name
+
+int
+pf_do_cmd(struct pfr_table *tbl, unsigned long cmd, void *buffer, int esize,
int items, int *nadd, int *ndel, int flags)
+{
+ struct pfioc_table io;
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ if (tbl)
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = buffer;
+ io.pfrio_esize = esize;
+ io.pfrio_size = items;
+
+ /* DBG("Doing PF ioctl %X for table %s on fd %d\n", cmd, tbl ?
tbl->pfrt_name : "NULL", pf_fd); */
+ if (ioctl(pf_fd, cmd, &io))
+ return 0;
+
+ if (nadd)
+ *nadd = io.pfrio_nadd;
+ if (ndel)
+ *ndel = io.pfrio_ndel;
+
+ return 1;
+}
+
+void *
+pf_fw_init(struct proto *p, char *table)
+{
+ pool *fwpool = p->pool;
+ struct pfr_table pf_table;
+ int nadd = 0;
+
+ if (strlen(table) > PF_TABLE_NAME_SIZE)
+ {
+ log(L_ERR "PF table name too long, max %d", PF_TABLE_NAME_SIZE);
+ return NULL;
+ }
+
+ memset(&pf_table, 0, sizeof(pf_table));
+
+ if (pf_fd == -1)
+ {
+ if ((pf_fd = open(PF_DEVNAME, O_RDWR)) == -1)
+ {
+ log(L_ERR "pf: error opening %s: %m", PF_DEVNAME);
+ return NULL;
+ }
+
+ DBG("Opened PF socked %d\n", pf_fd);
+ }
+
+ strcpy(pf_table.pfrt_name, table);
+ pf_table.pfrt_flags |= PFR_TFLAG_PERSIST;
+ if (!pf_do_cmd(NULL, DIOCRADDTABLES, &pf_table, sizeof(pf_table), 1, &nadd,
NULL, 0))
+ {
+ log(L_ERR "Error creating PF table %s: %m", table);
+ if (pf_instance_count == 0)
+ {
+ log(L_ERR "Closing PF socket");
+ close(pf_fd);
+ pf_fd = -1;
+ }
+ return NULL;
+ }
+ DBG("PF table %s created\n", table);
+ /* Remove persistent flag */
+ pf_table.pfrt_flags = 0;
+
+ struct pf_priv *priv = mb_alloc(fwpool, sizeof(struct pf_priv));
+
+ priv->table = pf_table;
+ priv->pool = fwpool;
+
+ pf_instance_count++;
+
+ return priv;
+}
+
+void
+pf_fw_shutdown(void *_priv UNUSED)
+{
+ if (--pf_instance_count == 0)
+ {
+ DBG("Closing PF socket %d\n", pf_fd);
+ close(pf_fd);
+ pf_fd = -1;
+ }
+}
+
+int
+pf_fw_flush(void *_priv)
+{
+ struct pf_priv *priv = _priv;
+ int ndel;
+
+ log(L_DEBUG "Flushing PF table %s", priv->pf_tablename);
+
+ if (!pf_do_cmd(&priv->table, DIOCRCLRADDRS, NULL, 0, 0, NULL, &ndel, 0))
+ {
+ log(L_ERR "Error flushing PF table %s: %m", priv->pf_tablename);
+ return 0;
+ }
+
+ DBG("Flushed %d record(s) from PF table %s\n", ndel, priv->pf_tablename);
+
+ return 1;
+}
+
+static int
+pf_put_addr(struct pfr_addr *pf_addr, net *n)
+{
+ int rt_family = AF_INET;
+ ip_addr addr;
+
+ memset(pf_addr, 0, sizeof(struct pfr_addr));
+ pf_addr->pfra_not = 0;
+ pf_addr->pfra_net = n->n.pxlen;
+ switch (rt_family)
+ {
+ case AF_INET:
+ addr = n->n.prefix;
+ ipa_hton(addr);
+ pf_addr->pfra_ip4addr.s_addr = addr;
+ pf_addr->pfra_af = rt_family;
+ break;
+ default:
+ log(L_ERR "Address family %d is not supported by pf, ignoring prefix",
rt_family);
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+pf_fw_add(void *_priv, net *n, char *prefixdata)
+{
+ struct pf_priv *priv = _priv;
+ struct pfr_addr pf_addr;
+ int nadd = 0;
+
+ if (!pf_put_addr(&pf_addr, n))
+ {
+ FW_ERR("Error adding %I/%d to PF table %s", n->n.prefix, n->n.pxlen,
priv->pf_tablename);
+ return 0;
+ }
+
+ DBG("Adding %I/%d to PF table %s with value %s\n", n->n.prefix, n->n.pxlen,
priv->pf_tablename, prefixdata);
+ if (!pf_do_cmd(&priv->table, DIOCRADDADDRS, &pf_addr, sizeof(pf_addr), 1,
&nadd, NULL, 0))
+ {
+ FW_ERR("Error adding %I/%d to PF table %s: %m", n->n.prefix, n->n.pxlen,
priv->pf_tablename);
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+pf_fw_del(void *_priv, net *n)
+{
+ struct pf_priv *priv = _priv;
+ struct pfr_addr pf_addr;
+ int ndel = 0;
+
+ if (!pf_put_addr(&pf_addr, n))
+ {
+ FW_ERR("Error deleting %I/%d from PF table %s", n->n.prefix, n->n.pxlen,
priv->pf_tablename);
+ return 0;
+ }
+
+ DBG("Deleting %I/%d from PF table %s\n", n->n.prefix, n->n.pxlen,
priv->pf_tablename);
+ if (!pf_do_cmd(&priv->table, DIOCRDELADDRS, &pf_addr, sizeof(pf_addr), 1,
NULL, &ndel, 0))
+ {
+ FW_ERR("Error deleting %I/%d from PF table %s: %m", n->n.prefix,
n->n.pxlen, priv->pf_tablename);
+ return 0;
+ }
+
+ return 1;
+}
+
+struct firewall_control fw_pf = {
+ fwtype: FWTYPE_PF,
+ description: "PF",
+ fw_init: pf_fw_init,
+ fw_shutdown: pf_fw_shutdown,
+ fw_flush: pf_fw_flush,
+ fw_add: pf_fw_add,
+ fw_del: pf_fw_del,
+};
+#endif
+
+
+#endif
+
--
1.7.3.2