summaryrefslogtreecommitdiffhomepage
path: root/contrib/fwd/src/fwd_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/fwd/src/fwd_config.c')
-rw-r--r--contrib/fwd/src/fwd_config.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/contrib/fwd/src/fwd_config.c b/contrib/fwd/src/fwd_config.c
new file mode 100644
index 000000000..1d16606d3
--- /dev/null
+++ b/contrib/fwd/src/fwd_config.c
@@ -0,0 +1,870 @@
+/*
+ * fwd - OpenWrt firewall daemon - config parsing
+ *
+ * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ * The fwd program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * The fwd program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the fwd program. If not, see http://www.gnu.org/licenses/.
+ */
+
+
+#include "fwd.h"
+#include "fwd_addr.h"
+#include "fwd_config.h"
+
+#include "ucix.h"
+
+
+#define fwd_read_error(...) do { \
+ fprintf(stderr, "ERROR: "); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ return; \
+} while(0)
+
+
+/*
+ * Parse helpers
+ */
+static int
+fwd_read_policy(struct uci_context *uci, const char *s, const char *o)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+
+ if( val != NULL )
+ {
+ switch( val[0] )
+ {
+ case 'D':
+ case 'd':
+ return FWD_P_DROP;
+
+ case 'R':
+ case 'r':
+ return FWD_P_REJECT;
+
+ case 'A':
+ case 'a':
+ return FWD_P_ACCEPT;
+ }
+ }
+
+ return FWD_P_UNSPEC;
+}
+
+static int
+fwd_read_bool(struct uci_context *uci, const char *s, const char *o, int d)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+
+ if( val != NULL )
+ {
+ if( !strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1") )
+ return 1;
+ else
+ return 0;
+ }
+
+ return d;
+}
+
+static unsigned int
+fwd_read_uint(struct uci_context *uci, const char *s, const char *o, unsigned int d)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+
+ if( val != NULL )
+ {
+ return atoi(val);
+ }
+
+ return d;
+}
+
+static int
+fwd_read_cidr(struct uci_context *uci, const char *s, const char *o, struct fwd_cidr **c)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+ char ip[32], prefix[32];
+ struct in_addr ina;
+
+ memset(ip, 0, 32);
+ memset(prefix, 0, 32);
+
+ if( val == NULL )
+ {
+ return 0;
+ }
+ else if( (strlen(val) < 32) && (sscanf(val, "%[^/]/%s", ip, prefix) > 0) )
+ {
+ if( !(*c = fwd_alloc_ptr(struct fwd_cidr)) )
+ goto inval;
+
+ if( inet_aton(ip, &ina) )
+ {
+ (*c)->addr.s_addr = ina.s_addr;
+
+ if( strchr(prefix, '.') )
+ {
+ if( inet_aton(prefix, &ina) )
+ {
+ (*c)->prefix = 32;
+ ina.s_addr = ntohl(ina.s_addr);
+
+ while( !(ina.s_addr & 1) )
+ {
+ ina.s_addr >>= 1;
+ (*c)->prefix--;
+ }
+ }
+ else
+ {
+ goto inval;
+ }
+ }
+ else
+ {
+ (*c)->prefix = prefix[0] ? atoi(prefix) : 32;
+
+ if( ((*c)->prefix < 0) || ((*c)->prefix > 32) )
+ {
+ goto inval;
+ }
+ }
+
+ return 0;
+ }
+ }
+
+ inval:
+ fwd_free_ptr(*c);
+ return -1;
+}
+
+static int
+fwd_read_mac(struct uci_context *uci, const char *s, const char *o, struct fwd_mac **m)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+
+ if( val == NULL )
+ {
+ return 0;
+ }
+ else
+ {
+ if( (*m = fwd_alloc_ptr(struct fwd_mac)) != NULL )
+ {
+ if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
+ (unsigned int *)&(*m)->mac[0], (unsigned int *)&(*m)->mac[1],
+ (unsigned int *)&(*m)->mac[2], (unsigned int *)&(*m)->mac[3],
+ (unsigned int *)&(*m)->mac[4], (unsigned int *)&(*m)->mac[5]) == 6
+ ) {
+ return 0;
+ }
+ }
+ }
+
+ fwd_free_ptr(*m);
+ return -1;
+}
+
+static int
+fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+ int min = -1;
+ int max = -1;
+ unsigned int tmp;
+
+ if( val == NULL )
+ {
+ return 0;
+ }
+ else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
+ {
+ if( max == -1 )
+ {
+ max = min;
+ }
+ else if( min > max )
+ {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
+ {
+ if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
+ {
+ (*p)->min = min;
+ (*p)->max = max;
+ return 0;
+ }
+ }
+ }
+
+ fwd_free_ptr(*p);
+ return -1;
+}
+
+static int
+fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+ int proto;
+
+ if( val == NULL )
+ {
+ return 0;
+ }
+ else
+ {
+ if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
+ {
+ proto = atoi(val);
+
+ if( !strcasecmp(val, "all") )
+ {
+ (*p)->type = FWD_PR_ALL;
+ (*p)->proto = 0;
+ }
+ else if( !strcasecmp(val, "icmp") )
+ {
+ (*p)->type = FWD_PR_ICMP;
+ (*p)->proto = 0;
+ }
+ else if( !strcasecmp(val, "udp") )
+ {
+ (*p)->type = FWD_PR_UDP;
+ (*p)->proto = 0;
+ }
+ else if( !strcasecmp(val, "tcp") )
+ {
+ (*p)->type = FWD_PR_TCP;
+ (*p)->proto = 0;
+ }
+ else if( !strcasecmp(val, "tcpudp") )
+ {
+ (*p)->type = FWD_PR_TCPUDP;
+ (*p)->proto = 0;
+ }
+ else if( proto > 0 )
+ {
+ (*p)->type = FWD_PR_CUSTOM;
+ (*p)->proto = proto;
+ }
+ else
+ {
+ goto inval;
+ }
+
+ return 0;
+ }
+ }
+
+ inval:
+ fwd_free_ptr(*p);
+ return -1;
+}
+
+static int
+fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
+{
+ const char *val = ucix_get_option(uci, "firewall", s, o);
+ unsigned int type, code;
+
+ if( val == NULL )
+ {
+ return 0;
+ }
+ else
+ {
+ if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
+ {
+ if( sscanf(val, "%u/%u", &type, &code) == 2 )
+ {
+ if( (type > 255) || (code > 255) )
+ goto inval;
+
+ (*i)->type = type;
+ (*i)->code = code;
+
+ return 0;
+ }
+
+ else if( sscanf(val, "%u", &type) == 1 )
+ {
+ if( type > 255 )
+ goto inval;
+
+ (*i)->type = type;
+ (*i)->code = -1;
+
+ return 0;
+ }
+
+ /* XXX: no validity check here but I do not want to
+ duplicate libipt_icmp.c ... */
+ else if( sscanf(val, "%31s", (*i)->name) == 1 )
+ {
+ return 0;
+ }
+ }
+ }
+
+ inval:
+ fwd_free_ptr(*i);
+ return -1;
+}
+
+static const char *
+fwd_read_string(struct uci_context *uci, const char *s, const char *o)
+{
+ return ucix_get_option(uci, "firewall", s, o);
+}
+
+
+static void
+fwd_append_config(struct fwd_data *h, struct fwd_data *a)
+{
+ while( h->next )
+ h = h->next;
+
+ h->next = a;
+}
+
+
+/*
+ * config defaults
+ */
+static void fwd_read_defaults_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_defaults *d
+) {
+ d->input = fwd_read_policy(uci, s, "input");
+ d->forward = fwd_read_policy(uci, s, "forward");
+ d->output = fwd_read_policy(uci, s, "output");
+ d->syn_flood = fwd_read_bool(uci, s, "syn_flood", 1);
+ d->syn_rate = fwd_read_uint(uci, s, "syn_rate", 25);
+ d->syn_burst = fwd_read_uint(uci, s, "syn_burst", 50);
+ d->drop_invalid = fwd_read_bool(uci, s, "drop_invalid", 1);
+}
+
+static struct fwd_data *
+fwd_read_defaults(struct uci_context *uci)
+{
+ struct fwd_data *dt;
+ struct fwd_defaults d;
+
+ if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ memset(&d, 0, sizeof(d));
+
+ ucix_for_each_section_type(uci, "firewall", "defaults",
+ (void *)fwd_read_defaults_cb, &d);
+
+ memcpy(&dt->section.defaults, &d, sizeof(d));
+
+ dt->type = FWD_S_DEFAULTS;
+ dt->next = NULL;
+
+ return dt;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * config zone
+ */
+static void fwd_read_zone_networks_cb(
+ const char *net, struct fwd_network_list **np
+) {
+ struct fwd_network_list *nn;
+
+ if( (nn = fwd_alloc_ptr(struct fwd_network_list)) != NULL )
+ {
+ nn->name = strdup(net);
+ nn->next = *np;
+ *np = nn;
+ }
+}
+
+static void fwd_read_zones_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_data_conveyor *cv
+) {
+ struct fwd_data *dtn;
+ struct fwd_network_list *net = NULL;
+ const char *name;
+
+ if( !(name = fwd_read_string(uci, s, "name")) )
+ fwd_read_error("section '%s' is missing 'name' option!", s);
+
+ if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ dtn->section.zone.name = strdup(name);
+ dtn->section.zone.masq = fwd_read_bool(uci, s, "masq", 0);
+ dtn->section.zone.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
+ dtn->section.zone.conntrack = fwd_read_bool(uci, s, "conntrack", 0);
+
+ dtn->section.zone.input = fwd_read_policy(uci, s, "input")
+ ?: cv->head->section.defaults.input ?: FWD_P_DROP;
+
+ dtn->section.zone.forward = fwd_read_policy(uci, s, "forward")
+ ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
+
+ dtn->section.zone.output = fwd_read_policy(uci, s, "output")
+ ?: cv->head->section.defaults.output ?: FWD_P_DROP;
+
+ /* try to parse option/list network ... */
+ if( ucix_for_each_list(uci, "firewall", s, "network",
+ (void *)&fwd_read_zone_networks_cb, &net) < 0 )
+ {
+ /* ... didn't work, fallback to option name */
+ fwd_read_zone_networks_cb(name, &net);
+ }
+
+ dtn->section.zone.networks = net;
+ dtn->type = FWD_S_ZONE;
+ dtn->next = cv->cursor;
+ cv->cursor = dtn;
+ }
+}
+
+static struct fwd_data *
+fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
+{
+ struct fwd_data_conveyor cv;
+
+ cv.cursor = NULL;
+ cv.head = def;
+
+ ucix_for_each_section_type(uci, "firewall", "zone",
+ (void *)fwd_read_zones_cb, &cv);
+
+ return cv.cursor;
+}
+
+
+/*
+ * config forwarding
+ */
+static void fwd_read_forwards_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_data_conveyor *cv
+) {
+ const char *src, *dest;
+ struct fwd_data *dtn;
+ struct fwd_zone *zsrc = NULL;
+ struct fwd_zone *zdest = NULL;
+
+ if( (src = fwd_read_string(uci, s, "src")) != NULL )
+ {
+ if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
+ fwd_read_error("section '%s' references unknown src zone '%s'!", s, src);
+ }
+
+ if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
+ {
+ if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
+ fwd_read_error("section '%s' references unknown dest zone '%s'!", s, dest);
+ }
+
+ if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ dtn->section.forwarding.src = zsrc;
+ dtn->section.forwarding.dest = zdest;
+ dtn->section.forwarding.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
+ dtn->section.forwarding.masq = fwd_read_bool(uci, s, "masq", 0);
+
+ dtn->type = FWD_S_FORWARD;
+ dtn->next = cv->cursor;
+ cv->cursor = dtn;
+ }
+ else
+ {
+ fwd_read_error("out of memory while parsing config!");
+ }
+}
+
+static struct fwd_data *
+fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
+{
+ struct fwd_data_conveyor cv;
+
+ cv.cursor = NULL;
+ cv.head = zones;
+
+ ucix_for_each_section_type(uci, "firewall", "forwarding",
+ (void *)fwd_read_forwards_cb, &cv);
+
+ return cv.cursor;
+}
+
+
+/*
+ * config redirect
+ */
+static void fwd_read_redirects_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_data_conveyor *cv
+) {
+ const char *src;
+ struct fwd_data *dtn = NULL;
+ struct fwd_zone *zsrc = NULL;
+
+ /* check zone */
+ if( !(src = fwd_read_string(uci, s, "src")) )
+ fwd_read_error(
+ "section '%s' is missing 'src' option!",
+ s
+ );
+
+ else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
+ fwd_read_error(
+ "section '%s' references unknown src zone '%s'!",
+ s, src
+ );
+
+ /* uci context, section, name, type */
+ fwd_check_option(uci, s, src_ip, cidr);
+ fwd_check_option(uci, s, src_mac, mac);
+ fwd_check_option(uci, s, src_port, portrange);
+ fwd_check_option(uci, s, src_dport, portrange);
+ fwd_check_option(uci, s, dest_ip, cidr);
+ fwd_check_option(uci, s, dest_port, portrange);
+ fwd_check_option(uci, s, proto, proto);
+
+ if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ dtn->section.redirect.proto = proto;
+ dtn->section.redirect.src = zsrc;
+ dtn->section.redirect.src_ip = src_ip;
+ dtn->section.redirect.src_mac = src_mac;
+ dtn->section.redirect.src_port = src_port;
+ dtn->section.redirect.src_dport = src_dport;
+ dtn->section.redirect.dest_ip = dest_ip;
+ dtn->section.redirect.dest_port = dest_port;
+
+ dtn->type = FWD_S_REDIRECT;
+ dtn->next = cv->cursor;
+ cv->cursor = dtn;
+ }
+ else
+ {
+ fwd_read_error("out of memory while parsing config!");
+ }
+}
+
+static struct fwd_data *
+fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
+{
+ struct fwd_data_conveyor cv;
+
+ cv.cursor = NULL;
+ cv.head = zones;
+
+ ucix_for_each_section_type(uci, "firewall", "redirect",
+ (void *)fwd_read_redirects_cb, &cv);
+
+ return cv.cursor;
+}
+
+
+/*
+ * config rule
+ */
+static void fwd_read_rules_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_data_conveyor *cv
+) {
+ const char *src, *dest;
+ struct fwd_data *dtn = NULL;
+ struct fwd_zone *zsrc = NULL;
+ struct fwd_zone *zdest = NULL;
+
+ /* check zones */
+ if( !(src = fwd_read_string(uci, s, "src")) )
+ fwd_read_error(
+ "section '%s' is missing 'src' option!",
+ s
+ );
+
+ else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
+ fwd_read_error(
+ "section '%s' references unknown src zone '%s'!",
+ s, src
+ );
+
+ if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
+ if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
+ fwd_read_error(
+ "section '%s' references unknown dest zone '%s'!",
+ s, dest
+ );
+
+ /* uci context, section, name, type */
+ fwd_check_option(uci, s, src_ip, cidr);
+ fwd_check_option(uci, s, src_mac, mac);
+ fwd_check_option(uci, s, src_port, portrange);
+ fwd_check_option(uci, s, dest_ip, cidr);
+ fwd_check_option(uci, s, dest_port, portrange);
+ fwd_check_option(uci, s, proto, proto);
+ fwd_check_option(uci, s, icmptype, icmptype);
+
+ if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ dtn->section.rule.proto = proto;
+ dtn->section.rule.icmp_type = icmptype;
+ dtn->section.rule.src = zsrc;
+ dtn->section.rule.src_ip = src_ip;
+ dtn->section.rule.src_mac = src_mac;
+ dtn->section.rule.src_port = src_port;
+ dtn->section.rule.dest = zdest;
+ dtn->section.rule.dest_ip = dest_ip;
+ dtn->section.rule.dest_port = dest_port;
+ dtn->section.rule.target = fwd_read_policy(uci, s, "target");
+
+ dtn->type = FWD_S_RULE;
+ dtn->next = cv->cursor;
+ cv->cursor = dtn;
+ }
+ else
+ {
+ fwd_read_error("out of memory while parsing config!");
+ }
+}
+
+static struct fwd_data *
+fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
+{
+ struct fwd_data_conveyor cv;
+
+ cv.cursor = NULL;
+ cv.head = zones;
+
+ ucix_for_each_section_type(uci, "firewall", "rule",
+ (void *)fwd_read_rules_cb, &cv);
+
+ return cv.cursor;
+}
+
+
+/*
+ * config include
+ */
+static void fwd_read_includes_cb(
+ struct uci_context *uci,
+ const char *s, struct fwd_data_conveyor *cv
+) {
+ const char *path = fwd_read_string(uci, s, "path");
+ struct fwd_data *dtn = NULL;
+
+ if( path != NULL )
+ {
+ if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
+ {
+ dtn->section.include.path = strdup(path);
+
+ dtn->type = FWD_S_INCLUDE;
+ dtn->next = cv->cursor;
+ cv->cursor = dtn;
+ }
+ else
+ {
+ fwd_read_error("out of memory while parsing config!");
+ }
+ }
+}
+
+static struct fwd_data *
+fwd_read_includes(struct uci_context *uci)
+{
+ struct fwd_data_conveyor cv;
+
+ cv.cursor = NULL;
+ cv.head = NULL;
+
+ ucix_for_each_section_type(uci, "firewall", "include",
+ (void *)fwd_read_includes_cb, &cv);
+
+ return cv.cursor;
+}
+
+
+/*
+ * config interface
+ */
+static void fwd_read_network_data(
+ struct uci_context *uci, struct fwd_network_list *net
+) {
+ struct fwd_network_list *e;
+ const char *type, *ifname;
+
+ for( e = net; e; e = e->next )
+ {
+ if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
+ {
+ if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
+ fwd_read_error(
+ "section '%s' is missing 'ifname' option!",
+ e->name
+ );
+
+ e->isalias = (strcmp(type, "alias") ? 0 : 1);
+ e->ifname = strdup(ifname);
+ }
+ }
+}
+
+static void fwd_read_networks(
+ struct uci_context *uci, struct fwd_data *zones
+) {
+ struct fwd_data *e;
+
+ for( e = zones; e; e = e->next )
+ if( e->type == FWD_S_ZONE )
+ fwd_read_network_data(uci, e->section.zone.networks);
+}
+
+static void fwd_free_networks(struct fwd_network_list *h)
+{
+ struct fwd_network_list *e = h;
+
+ while( h != NULL )
+ {
+ e = h->next;
+
+ fwd_free_ptr(h->name);
+ fwd_free_ptr(h->ifname);
+ fwd_free_ptr(h->addr);
+
+ free(h);
+ h = e;
+ }
+
+ e = h = NULL;
+}
+
+
+
+struct fwd_data * fwd_read_config(void)
+{
+ struct uci_context *ctx;
+ struct fwd_data *defaults, *zones;
+
+ if( (ctx = ucix_init("firewall")) != NULL )
+ {
+ if( !(defaults = fwd_read_defaults(ctx)) )
+ goto error;
+
+ if( !(zones = fwd_read_zones(ctx, defaults)) )
+ goto error;
+
+ fwd_append_config(defaults, zones);
+ fwd_append_config(defaults, fwd_read_forwards(ctx, zones));
+ fwd_append_config(defaults, fwd_read_redirects(ctx, zones));
+ fwd_append_config(defaults, fwd_read_rules(ctx, zones));
+ fwd_append_config(defaults, fwd_read_includes(ctx));
+
+ ucix_cleanup(ctx);
+
+ if( (ctx = ucix_init("network")) != NULL )
+ {
+ fwd_read_networks(ctx, zones);
+ ucix_cleanup(ctx);
+
+ return defaults;
+ }
+ }
+
+ error:
+ if( ctx ) ucix_cleanup(ctx);
+ fwd_free_config(defaults);
+ fwd_free_config(zones);
+ return NULL;
+}
+
+
+void fwd_free_config(struct fwd_data *h)
+{
+ struct fwd_data *e = h;
+
+ while( h != NULL )
+ {
+ e = h->next;
+
+ switch(h->type)
+ {
+ case FWD_S_INCLUDE:
+ fwd_free_ptr(h->section.include.path);
+ break;
+
+ case FWD_S_ZONE:
+ fwd_free_ptr(h->section.zone.name);
+ fwd_free_networks(h->section.zone.networks);
+ break;
+
+ case FWD_S_REDIRECT:
+ fwd_free_ptr(h->section.redirect.src_ip);
+ fwd_free_ptr(h->section.redirect.src_mac);
+ fwd_free_ptr(h->section.redirect.src_port);
+ fwd_free_ptr(h->section.redirect.src_dport);
+ fwd_free_ptr(h->section.redirect.dest_ip);
+ fwd_free_ptr(h->section.redirect.dest_port);
+ fwd_free_ptr(h->section.redirect.proto);
+ break;
+
+ case FWD_S_RULE:
+ fwd_free_ptr(h->section.rule.src_ip);
+ fwd_free_ptr(h->section.rule.src_mac);
+ fwd_free_ptr(h->section.rule.src_port);
+ fwd_free_ptr(h->section.rule.dest_ip);
+ fwd_free_ptr(h->section.rule.dest_port);
+ fwd_free_ptr(h->section.rule.proto);
+ fwd_free_ptr(h->section.rule.icmp_type);
+ break;
+
+ case FWD_S_DEFAULTS:
+ case FWD_S_FORWARD:
+ /* Make gcc happy */
+ break;
+ }
+
+ free(h);
+ h = e;
+ }
+
+ e = h = NULL;
+}
+
+
+struct fwd_zone *
+fwd_lookup_zone(struct fwd_data *h, const char *n)
+{
+ struct fwd_data *e;
+
+ if( n != NULL )
+ {
+ for( e = h; e; e = e->next )
+ {
+ if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
+ return &e->section.zone;
+ }
+ }
+
+ return NULL;
+}
+