[PATCH 1/1] * Add firewall protocol

Alexander V. Chernikov melifaro at ipfw.ru
Sun Dec 11 18:05:37 CET 2011


---
 configure.in              |    6 +-
 doc/bird.sgml             |   35 ++++
 nest/proto.c              |    3 +
 nest/protocol.h           |    2 +-
 nest/route.h              |    3 +-
 proto/firewall/Doc        |    1 +
 proto/firewall/Makefile   |    6 +
 proto/firewall/config.Y   |   75 ++++++++
 proto/firewall/firewall.c |  201 ++++++++++++++++++++++
 proto/firewall/firewall.h |   52 ++++++
 sysdep/autoconf.h.in      |    5 +
 sysdep/bsd/Modules        |    1 +
 sysdep/bsd/fw.c           |  412 +++++++++++++++++++++++++++++++++++++++++++++
 13 files changed, 799 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..2db67d0 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -2490,6 +2490,41 @@ 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>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.
+</descrip>
+
+<p>Firewall defines single route attribute:
+
+<descrip>
+	<tag>int <cf/fw_value/</tag> Value that can be passed with prefix.
+	Value is signed 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
+	flush always;			 # Flush table on both startup and shutdown
+	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..5500ea5
--- /dev/null
+++ b/proto/firewall/config.Y
@@ -0,0 +1,75 @@
+/*
+ *	BIRD -- Firewall Protocol Configuration
+ *
+ *	(c) 2011 Alexander V. Chernikov <melifaro at 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, FLUSH, 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_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");
+	}
+	FIREWALL_CFG->fwtype = $2;
+  };
+ | FWTABLE TEXT { FIREWALL_CFG->fwtable = $2; }
+ | FLUSH ON STARTUP { FIREWALL_CFG->flush_start = 1; }
+ | FLUSH ON SHUTDOWN { FIREWALL_CFG->flush_shutdown = 1; }
+ | FLUSH ALWAYS { FIREWALL_CFG->flush_start = 1; FIREWALL_CFG->flush_shutdown = 1; }
+ ;
+
+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..1ba1677
--- /dev/null
+++ b/proto/firewall/firewall.c
@@ -0,0 +1,201 @@
+/*
+ *	BIRD -- Firewall Protocol Configuration
+ *
+ *	(c) 2011 Alexander V. Chernikov <melifaro at 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;
+
+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)
+    {
+      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);
+
+}
+
+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;
+      }
+
+  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 ? c->fwtable : "none");
+}
+
+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..d203a5c
--- /dev/null
+++ b/proto/firewall/firewall.h
@@ -0,0 +1,52 @@
+/*
+ *	BIRD -- Firewall Protocol Configuration
+ *
+ *	(c) 2011 Alexander V. Chernikov <melifaro at 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;
+
+#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..2f4c74d
--- /dev/null
+++ b/sysdep/bsd/fw.c
@@ -0,0 +1,412 @@
+/*
+ *	BIRD -- IPFW/PF manipulations
+ *
+ *	(c) 2011 Alexander V. Chernikov <melifaro at 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)
+{
+  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);
+}
+
+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)
+    {
+      log(L_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)
+    {
+      log(L_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)
+{
+  struct pf_priv *priv = _priv;
+
+  if (--pf_instance_count == 0)
+    {
+      DBG("Closing PF socket %d\n", pf_fd);
+      close(pf_fd);
+      pf_fd = -1;
+    }
+
+  mb_free(priv);
+}
+
+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))
+    {
+      log(L_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))
+    {
+      log(L_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))
+    {
+      log(L_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))
+    {
+      log(L_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


--------------000802080401010600010305--



More information about the Bird-users mailing list