diff options
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | doc/bird.sgml | 35 | ||||
-rw-r--r-- | nest/proto.c | 4 | ||||
-rw-r--r-- | nest/protocol.h | 3 | ||||
-rw-r--r-- | proto/firewall/Doc | 1 | ||||
-rw-r--r-- | proto/firewall/Makefile | 6 | ||||
-rw-r--r-- | proto/firewall/config.Y | 75 | ||||
-rw-r--r-- | proto/firewall/firewall.c | 201 | ||||
-rw-r--r-- | proto/firewall/firewall.h | 52 | ||||
-rw-r--r-- | sysdep/bsd/Makefile | 2 | ||||
-rw-r--r-- | sysdep/bsd/fw.c | 412 |
11 files changed, 793 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac index da1a8f44..c9555719 100644 --- a/configure.ac +++ b/configure.ac @@ -193,6 +193,8 @@ else sysdesc=bsd CPPFLAGS="$CPPFLAGS -I/usr/local/include" LDFLAGS="$LDFLAGS -L/usr/local/lib" + AC_DEFINE(CONFIG_FIREWALL_IPFW, 1) + AC_DEFINE(CONFIG_FIREWALL_PF, 1) ;; kfreebsd*) sysdesc=bsd @@ -201,9 +203,11 @@ else sysdesc=bsd CPPFLAGS="$CPPFLAGS -I/usr/pkg/include" LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib" + AC_DEFINE(CONFIG_FIREWALL_PF, 1) ;; openbsd*) sysdesc=bsd + AC_DEFINE(CONFIG_FIREWALL_PF, 1) ;; dragonfly*) sysdesc=bsd @@ -271,7 +275,7 @@ if test "$enable_mpls_kernel" != no ; then fi fi -all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static" +all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static firewall" all_protocols=`echo $all_protocols | sed 's/ /,/g'` diff --git a/doc/bird.sgml b/doc/bird.sgml index 9675caf0..f93ca14d 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -4970,6 +4970,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 <label id="conclusion"> diff --git a/nest/proto.c b/nest/proto.c index d4a333d0..040dc115 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1382,7 +1382,9 @@ protos_build(void) #ifdef CONFIG_PERF proto_build(&proto_perf); #endif - +#ifdef CONFIG_FIREWALL + proto_build(&proto_firewall); +#endif proto_pool = rp_new(&root_pool, "Protocols"); proto_shutdown_timer = tm_new(proto_pool); proto_shutdown_timer->hook = proto_shutdown_loop; diff --git a/nest/protocol.h b/nest/protocol.h index 56d66ed5..8d122135 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -53,6 +53,7 @@ enum protocol_class { PROTOCOL_RIP, PROTOCOL_RPKI, PROTOCOL_STATIC, + PROTOCOL_FIREWALL, PROTOCOL__MAX }; @@ -102,7 +103,7 @@ void protos_dump_all(void); extern struct protocol proto_device, proto_radv, proto_rip, proto_static, proto_mrt, proto_ospf, proto_perf, - proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki; + proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki, proto_firewall; /* * Routing Protocol Instance diff --git a/proto/firewall/Doc b/proto/firewall/Doc new file mode 100644 index 00000000..57793429 --- /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 00000000..a322ab6a --- /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 00000000..805fc3fe --- /dev/null +++ b/proto/firewall/config.Y @@ -0,0 +1,75 @@ +/* + * BIRD -- Firewall Protocol Configuration + * + * (c) 2011 Alexander V. Chernikov <<A HREF="http://trubka.network.cz/mailman/listinfo/bird-users">melifaro at FreeBSD.org</A>> + * + * 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 00000000..36172cf7 --- /dev/null +++ b/proto/firewall/firewall.c @@ -0,0 +1,201 @@ +/* + * BIRD -- Firewall Protocol Configuration + * + * (c) 2011 Alexander V. Chernikov <<A HREF="http://trubka.network.cz/mailman/listinfo/bird-users">melifaro at FreeBSD.org</A>> + * + * 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", + class: PROTOCOL_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 00000000..7b16301a --- /dev/null +++ b/proto/firewall/firewall.h @@ -0,0 +1,52 @@ +/* + * BIRD -- Firewall Protocol Configuration + * + * (c) 2011 Alexander V. Chernikov <<A HREF="http://trubka.network.cz/mailman/listinfo/bird-users">melifaro at FreeBSD.org</A>> + * + * 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/bsd/Makefile b/sysdep/bsd/Makefile index dfa32747..307d461f 100644 --- a/sysdep/bsd/Makefile +++ b/sysdep/bsd/Makefile @@ -1,4 +1,4 @@ -src := krt-sock.c +src := krt-sock.c fw.c obj := $(src-o-files) $(all-daemon) $(conf-y-targets): $(s)krt-sock.Y diff --git a/sysdep/bsd/fw.c b/sysdep/bsd/fw.c new file mode 100644 index 00000000..64275082 --- /dev/null +++ b/sysdep/bsd/fw.c @@ -0,0 +1,412 @@ +/* + * BIRD -- IPFW/PF manipulations + * + * (c) 2011 Alexander V. Chernikov <<A HREF="http://trubka.network.cz/mailman/listinfo/bird-users">melifaro at FreeBSD.org</A>> + * + * 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 + |