983 lines
24 KiB
Diff
983 lines
24 KiB
Diff
|
From c99266ef16e66f94f22a2f78dcea82c795c4611f Mon Sep 17 00:00:00 2001
|
||
|
From: Alexander V. Chernikov <melifaro@ipfw.ru>
|
||
|
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 configure.in configure.in
|
||
|
index 46a6ecd..bb5f445 100644
|
||
|
--- configure.in
|
||
|
+++ 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.])
|
||
|
;;
|
||
|
--- configure.orig 2012-01-20 21:04:39.000000000 +0400
|
||
|
+++ configure 2012-01-26 17:37:43.000000000 +0400
|
||
|
@@ -2336,7 +2336,7 @@
|
||
|
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
|
||
|
@@ -4372,10 +4372,13 @@
|
||
|
ipv4:netbsd*) sysdesc=bsd
|
||
|
CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
|
||
|
LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
|
||
|
+ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
|
||
|
;;
|
||
|
ipv6:freebsd*) sysdesc=bsd-v6
|
||
|
;;
|
||
|
ipv4:freebsd*) sysdesc=bsd
|
||
|
+ $as_echo "#define CONFIG_FIREWALL_IPFW 1" >>confdefs.h
|
||
|
+ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
|
||
|
;;
|
||
|
ipv6:kfreebsd*) sysdesc=bsd-v6
|
||
|
;;
|
||
|
@@ -4384,6 +4387,7 @@
|
||
|
ipv6:openbsd*) sysdesc=bsd-v6
|
||
|
;;
|
||
|
ipv4:openbsd*) sysdesc=bsd
|
||
|
+ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
|
||
|
;;
|
||
|
*) as_fn_error $? "Cannot determine correct system configuration. Please use --with-sysconfig to set it manually." "$LINENO" 5
|
||
|
;;--- doc/bird.sgml
|
||
|
+++ 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 nest/proto.c nest/proto.c
|
||
|
index d55c348..85bdb19 100644
|
||
|
--- nest/proto.c
|
||
|
+++ 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 nest/protocol.h nest/protocol.h
|
||
|
index a7518c2..d09a556 100644
|
||
|
--- nest/protocol.h
|
||
|
+++ 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 nest/route.h nest/route.h
|
||
|
index a4c0154..e5f18dd 100644
|
||
|
--- nest/route.h
|
||
|
+++ 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 proto/firewall/Doc proto/firewall/Doc
|
||
|
new file mode 100644
|
||
|
index 0000000..5779342
|
||
|
--- /dev/null
|
||
|
+++ proto/firewall/Doc
|
||
|
@@ -0,0 +1 @@
|
||
|
+S firewall.c
|
||
|
diff --git proto/firewall/Makefile proto/firewall/Makefile
|
||
|
new file mode 100644
|
||
|
index 0000000..a322ab6
|
||
|
--- /dev/null
|
||
|
+++ proto/firewall/Makefile
|
||
|
@@ -0,0 +1,6 @@
|
||
|
+source=firewall.c
|
||
|
+root-rel=../../
|
||
|
+dir-name=proto/firewall
|
||
|
+
|
||
|
+include ../../Rules
|
||
|
+
|
||
|
diff --git proto/firewall/config.Y proto/firewall/config.Y
|
||
|
new file mode 100644
|
||
|
index 0000000..aefc606
|
||
|
--- /dev/null
|
||
|
+++ proto/firewall/config.Y
|
||
|
@@ -0,0 +1,77 @@
|
||
|
+/*
|
||
|
+ * BIRD -- Firewall Protocol Configuration
|
||
|
+ *
|
||
|
+ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||
|
+ *
|
||
|
+ * 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 proto/firewall/firewall.c proto/firewall/firewall.c
|
||
|
new file mode 100644
|
||
|
index 0000000..e447470
|
||
|
--- /dev/null
|
||
|
+++ proto/firewall/firewall.c
|
||
|
@@ -0,0 +1,198 @@
|
||
|
+/*
|
||
|
+ * BIRD -- Firewall Protocol Configuration
|
||
|
+ *
|
||
|
+ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||
|
+ *
|
||
|
+ * 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 proto/firewall/firewall.h proto/firewall/firewall.h
|
||
|
new file mode 100644
|
||
|
index 0000000..c97ed38
|
||
|
--- /dev/null
|
||
|
+++ proto/firewall/firewall.h
|
||
|
@@ -0,0 +1,54 @@
|
||
|
+/*
|
||
|
+ * BIRD -- Firewall Protocol Configuration
|
||
|
+ *
|
||
|
+ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||
|
+ *
|
||
|
+ * 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 sysdep/autoconf.h.in sysdep/autoconf.h.in
|
||
|
index d029e2a..c1fcdf7 100644
|
||
|
--- sysdep/autoconf.h.in
|
||
|
+++ 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 sysdep/bsd/Modules sysdep/bsd/Modules
|
||
|
index 84abffd..77f26e3 100644
|
||
|
--- sysdep/bsd/Modules
|
||
|
+++ sysdep/bsd/Modules
|
||
|
@@ -4,3 +4,4 @@ sysio.h
|
||
|
krt-set.h
|
||
|
krt-sock.c
|
||
|
krt-sock.h
|
||
|
+fw.c
|
||
|
diff --git sysdep/bsd/fw.c sysdep/bsd/fw.c
|
||
|
new file mode 100644
|
||
|
index 0000000..e841e06
|
||
|
--- /dev/null
|
||
|
+++ sysdep/bsd/fw.c
|
||
|
@@ -0,0 +1,404 @@
|
||
|
+/*
|
||
|
+ * BIRD -- IPFW/PF manipulations
|
||
|
+ *
|
||
|
+ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
|
||
|
+ *
|
||
|
+ * 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
|
||
|
|