summaryrefslogtreecommitdiffhomepage
path: root/contrib/fwd
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/fwd')
-rw-r--r--contrib/fwd/src/Makefile14
-rw-r--r--contrib/fwd/src/fwd.c57
-rw-r--r--contrib/fwd/src/fwd.h201
-rw-r--r--contrib/fwd/src/fwd_addr.c150
-rw-r--r--contrib/fwd/src/fwd_addr.h52
-rw-r--r--contrib/fwd/src/fwd_config.c870
-rw-r--r--contrib/fwd/src/fwd_config.h29
-rw-r--r--contrib/fwd/src/fwd_rules.c517
-rw-r--r--contrib/fwd/src/fwd_rules.h44
-rw-r--r--contrib/fwd/src/ucix.c236
-rw-r--r--contrib/fwd/src/ucix.h50
11 files changed, 2220 insertions, 0 deletions
diff --git a/contrib/fwd/src/Makefile b/contrib/fwd/src/Makefile
new file mode 100644
index 000000000..3a7736d54
--- /dev/null
+++ b/contrib/fwd/src/Makefile
@@ -0,0 +1,14 @@
+CFLAGS := -g -Wall -I./uci -I./iptables-1.4.5/include
+LDFLAGS := -luci -liptc -L./iptables-1.4.5/libiptc/.libs
+
+fwd:
+ $(CC) $(CFLAGS) -c -o ucix.o ucix.c
+ $(CC) $(CFLAGS) -c -o fwd_addr.o fwd_addr.c
+ $(CC) $(CFLAGS) -c -o fwd_rules.o fwd_rules.c
+ $(CC) $(CFLAGS) -c -o fwd_config.o fwd_config.c
+ $(CC) $(CFLAGS) -c -o fwd.o fwd.c
+ $(CC) $(LDFLAGS) -o fwd fwd.o fwd_addr.o fwd_rules.o fwd_config.o ucix.o
+
+clean:
+ rm -f *~ fwd *.o
+
diff --git a/contrib/fwd/src/fwd.c b/contrib/fwd/src/fwd.c
new file mode 100644
index 000000000..44b7f5a12
--- /dev/null
+++ b/contrib/fwd/src/fwd.c
@@ -0,0 +1,57 @@
+/*
+ * fwd - OpenWrt firewall daemon - main part
+ *
+ * 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_rules.h"
+#include "fwd_config.h"
+
+#define IPT "iptables"
+
+
+int main(int argc, const char *argv[])
+{
+ struct fwd_handle *h;
+
+ if( !(h = fwd_alloc_ptr(struct fwd_handle)) )
+ fwd_fatal("Out of memory");
+
+ if( !(h->conf = fwd_read_config()) )
+ fwd_fatal("Failed to read configuration");
+
+ if( (h->rtnl_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1 )
+ fwd_fatal("Failed to create AF_NETLINK socket (%m)");
+
+ if( !(h->addrs = fwd_get_addrs(h->rtnl_socket, AF_INET)) )
+ fwd_fatal("Failed to issue RTM_GETADDR (%m)");
+
+
+ fwd_ipt_build_ruleset(h);
+
+ fwd_ipt_addif(h, "lan");
+ fwd_ipt_addif(h, "wan");
+
+
+ close(h->rtnl_socket);
+ fwd_free_config(h->conf);
+ fwd_free_addrs(h->addrs);
+ fwd_free_ptr(h);
+
+ return 0;
+}
diff --git a/contrib/fwd/src/fwd.h b/contrib/fwd/src/fwd.h
new file mode 100644
index 000000000..c93c0aff9
--- /dev/null
+++ b/contrib/fwd/src/fwd.h
@@ -0,0 +1,201 @@
+/*
+ * fwd - OpenWrt firewall daemon - data structures
+ *
+ * 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/.
+ */
+
+#ifndef __FWD_H__
+#define __FWD_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+
+#if 0
+#include "fwd_addr.h"
+#include "fwd_rules.h"
+#include "fwd_config.h"
+#endif
+
+enum fwd_policy {
+ FWD_P_UNSPEC = 0,
+ FWD_P_DROP = 1,
+ FWD_P_REJECT = 2,
+ FWD_P_ACCEPT = 3
+};
+
+enum fwd_stype {
+ FWD_S_DEFAULTS = 0,
+ FWD_S_ZONE = 1,
+ FWD_S_FORWARD = 2,
+ FWD_S_REDIRECT = 3,
+ FWD_S_RULE = 4,
+ FWD_S_INCLUDE = 5
+};
+
+enum fwd_ptype {
+ FWD_PR_CUSTOM = 0,
+ FWD_PR_TCP = 1,
+ FWD_PR_UDP = 2,
+ FWD_PR_TCPUDP = 3,
+ FWD_PR_ICMP = 4,
+ FWD_PR_ALL = 5
+};
+
+struct fwd_portrange {
+ unsigned short min;
+ unsigned short max;
+};
+
+struct fwd_cidr {
+ struct in_addr addr;
+ int prefix;
+};
+
+struct fwd_mac {
+ unsigned char mac[6];
+};
+
+struct fwd_proto {
+ enum fwd_ptype type;
+ int proto;
+};
+
+struct fwd_icmptype {
+ char name[32];
+ int type;
+ int code;
+};
+
+struct fwd_network_list {
+ char *name;
+ char *ifname;
+ int isalias;
+ struct fwd_cidr *addr;
+ struct fwd_network_list *next;
+};
+
+struct fwd_defaults {
+ enum fwd_policy input;
+ enum fwd_policy forward;
+ enum fwd_policy output;
+ int syn_flood;
+ int syn_rate;
+ int syn_burst;
+ int drop_invalid;
+};
+
+struct fwd_zone {
+ char *name;
+ struct fwd_network_list *networks;
+ enum fwd_policy input;
+ enum fwd_policy forward;
+ enum fwd_policy output;
+ int masq;
+ int mtu_fix;
+ int conntrack;
+};
+
+struct fwd_forwarding {
+ struct fwd_zone *src;
+ struct fwd_zone *dest;
+ int mtu_fix; /* legacy */
+ int masq; /* new */
+};
+
+struct fwd_redirect {
+ struct fwd_zone *src;
+ struct fwd_cidr *src_ip;
+ struct fwd_mac *src_mac;
+ struct fwd_portrange *src_port;
+ struct fwd_portrange *src_dport;
+ struct fwd_cidr *dest_ip;
+ struct fwd_portrange *dest_port;
+ struct fwd_proto *proto;
+};
+
+struct fwd_rule {
+ struct fwd_zone *src;
+ struct fwd_zone *dest;
+ struct fwd_cidr *src_ip;
+ struct fwd_mac *src_mac;
+ struct fwd_portrange *src_port;
+ struct fwd_cidr *dest_ip;
+ struct fwd_portrange *dest_port;
+ struct fwd_proto *proto;
+ struct fwd_icmptype *icmp_type;
+ enum fwd_policy target;
+};
+
+struct fwd_include {
+ char *path;
+};
+
+struct fwd_data {
+ enum fwd_stype type;
+ struct fwd_data *next;
+ union {
+ struct fwd_defaults defaults;
+ struct fwd_zone zone;
+ struct fwd_forwarding forwarding;
+ struct fwd_redirect redirect;
+ struct fwd_rule rule;
+ struct fwd_include include;
+ } section;
+};
+
+
+struct fwd_handle {
+ int rtnl_socket;
+ struct fwd_data *conf;
+ struct fwd_addr_list *addrs;
+};
+
+
+/* fwd_zmalloc(size_t)
+ * Allocates a zeroed buffer of the given size. */
+static void * fwd_zmalloc(size_t s)
+{
+ void *b = malloc(s);
+
+ if( b != NULL )
+ memset(b, 0, s);
+
+ return b;
+}
+
+/* fwd_fatal(fmt, ...)
+ * Prints message to stderr and termintes program. */
+#define fwd_fatal(...) do { \
+ fprintf(stderr, "ERROR: "); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ exit(1); \
+} while(0)
+
+/* fwd_alloc_ptr(type)
+ * Allocates a buffer with the size of the given datatype
+ * and returns a pointer to it. */
+#define fwd_alloc_ptr(t) (t *) fwd_zmalloc(sizeof(t))
+
+/* fwd_free_ptr(void *)
+ * Frees the given pointer and sets it to NULL.
+ * Safe for NULL values. */
+#define fwd_free_ptr(x) do { if(x != NULL) free(x); x = NULL; } while(0)
+
+#endif
diff --git a/contrib/fwd/src/fwd_addr.c b/contrib/fwd/src/fwd_addr.c
new file mode 100644
index 000000000..df5d8e7c2
--- /dev/null
+++ b/contrib/fwd/src/fwd_addr.c
@@ -0,0 +1,150 @@
+/*
+ * fwd - OpenWrt firewall daemon - rtnetlink communication
+ *
+ * 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"
+
+struct fwd_addr_list * fwd_get_addrs(int fd, int family)
+{
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg r;
+ } req;
+
+ int offlen;
+ int rtattrlen;
+ int dump_done;
+ char buf[16384];
+
+ struct rtattr *rta;
+ struct rtattr *rtatp;
+ struct nlmsghdr *nlmp;
+ struct ifaddrmsg *rtmp;
+
+ struct fwd_addr_list *head, *entry;
+
+ /* Build request */
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+ req.n.nlmsg_type = RTM_GETADDR;
+ req.r.ifa_family = family;
+
+ rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
+ rta->rta_len = RTA_LENGTH(family == AF_INET ? 4 : 16);
+
+ head = entry = NULL;
+
+ /* Send request */
+ if( send(fd, &req, sizeof(req), 0) <= 0 )
+ goto error;
+
+ /* Receive responses */
+ for( dump_done = 0; !dump_done; )
+ {
+ if( (offlen = recv(fd, buf, sizeof(buf), 0)) <= 0 )
+ goto error;
+
+ /* Parse message */
+ for(nlmp = (struct nlmsghdr *)buf; offlen > sizeof(*nlmp);)
+ {
+ /* Dump finished? */
+ if( nlmp->nlmsg_type == NLMSG_DONE )
+ {
+ dump_done = 1;
+ break;
+ }
+
+ int len = nlmp->nlmsg_len;
+ int req_len = len - sizeof(*nlmp);
+
+ if( req_len<0 || len>offlen )
+ goto error;
+
+ if( !NLMSG_OK(nlmp, offlen) )
+ goto error;
+
+ rtmp = (struct ifaddrmsg *) NLMSG_DATA(nlmp);
+ rtatp = (struct rtattr *) IFA_RTA(rtmp);
+
+ if( !(entry = fwd_alloc_ptr(struct fwd_addr_list)) )
+ goto error;
+
+ entry->index = rtmp->ifa_index;
+ if_indextoname(rtmp->ifa_index, (char *)&entry->ifname);
+
+ rtattrlen = IFA_PAYLOAD(nlmp);
+
+ for( ; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen) )
+ {
+ if( rtatp->rta_type == IFA_ADDRESS )
+ {
+ memcpy(&entry->ipaddr, (char *) RTA_DATA(rtatp), rtatp->rta_len);
+ entry->prefix = rtmp->ifa_prefixlen;
+ entry->family = family;
+ }
+ else if( rtatp->rta_type == IFA_LABEL)
+ {
+ memcpy(&entry->label, (char *) RTA_DATA(rtatp), rtatp->rta_len);
+ }
+ }
+
+ entry->next = head;
+ head = entry;
+
+ offlen -= NLMSG_ALIGN(len);
+ nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
+ }
+ }
+
+ return head;
+
+
+ error:
+
+ fwd_free_addrs(head);
+ head = entry = NULL;
+
+ return NULL;
+}
+
+void fwd_free_addrs(struct fwd_addr_list *head)
+{
+ struct fwd_addr_list *entry = head;
+
+ while( entry != NULL )
+ {
+ head = entry->next;
+ free(entry);
+ entry = head;
+ }
+
+ head = entry = NULL;
+}
+
+struct fwd_addr_list * fwd_append_addrs(struct fwd_addr_list *head, struct fwd_addr_list *add)
+{
+ struct fwd_addr_list *entry = head;
+
+ while( entry->next != NULL )
+ entry = entry->next;
+
+ return (entry->next = add);
+}
+
diff --git a/contrib/fwd/src/fwd_addr.h b/contrib/fwd/src/fwd_addr.h
new file mode 100644
index 000000000..1a28f6464
--- /dev/null
+++ b/contrib/fwd/src/fwd_addr.h
@@ -0,0 +1,52 @@
+/*
+ * fwd - OpenWrt firewall daemon - header for rtnetlink communication
+ *
+ * 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/.
+ */
+
+#ifndef __FWD_ADDR_H__
+#define __FWD_ADDR_H__
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <arpa/inet.h>
+
+
+struct fwd_addr_list {
+ char ifname[IFNAMSIZ];
+ char label[IFNAMSIZ];
+ int family;
+ int index;
+ unsigned int prefix;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } ipaddr;
+ struct fwd_addr_list *next;
+};
+
+
+struct fwd_addr_list * fwd_get_addrs(int, int);
+struct fwd_addr_list * fwd_append_addrs(struct fwd_addr_list *, struct fwd_addr_list *);
+void fwd_free_addrs(struct fwd_addr_list *);
+
+#define fwd_foreach_addrs(head, entry) for(entry = head; entry; entry = entry->next)
+
+#endif
+
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;
+}
+
diff --git a/contrib/fwd/src/fwd_config.h b/contrib/fwd/src/fwd_config.h
new file mode 100644
index 000000000..ba66dffea
--- /dev/null
+++ b/contrib/fwd/src/fwd_config.h
@@ -0,0 +1,29 @@
+#ifndef __FWD_CONFIG_H__
+#define __FWD_CONFIG_H__
+
+#include "fwd.h"
+#include "ucix.h"
+
+/* fwd_check_option(uci_ctx, section, name, type) */
+#define fwd_check_option(uci, sct, name, type) \
+ struct fwd_##type *name = NULL; \
+ if( fwd_read_##type(uci, sct, #name, &name) ) \
+ { \
+ printf("ERROR: section '%s' contains invalid %s in '%s'!\n", \
+ sct, #type, #name); \
+ return; \
+ }
+
+/* structure to access fwd_data* in uci iter callbacks */
+struct fwd_data_conveyor {
+ struct fwd_data *head;
+ struct fwd_data *cursor;
+};
+
+/* api */
+struct fwd_data * fwd_read_config(void);
+struct fwd_zone * fwd_lookup_zone(struct fwd_data *, const char *);
+
+void fwd_free_config(struct fwd_data *);
+
+#endif
diff --git a/contrib/fwd/src/fwd_rules.c b/contrib/fwd/src/fwd_rules.c
new file mode 100644
index 000000000..9c233417e
--- /dev/null
+++ b/contrib/fwd/src/fwd_rules.c
@@ -0,0 +1,517 @@
+/*
+ * fwd - OpenWrt firewall daemon - iptables rule set
+ *
+ * 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_rules.h"
+
+static void
+fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...)
+{
+ int len = 0;
+ char buf[256]; buf[0] = 0;
+
+ va_list ap;
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if( len > 0 )
+ {
+ r->buf = realloc(r->buf, r->len + len + 1);
+ memcpy(&r->buf[r->len], buf, len);
+ r->buf[r->len + len] = 0;
+ r->len += len;
+ }
+}
+
+static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table)
+{
+ struct fwd_ipt_rulebuf *r;
+
+ if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL )
+ {
+ fwd_ipt_rule_append(r, IPT " -t %s", table);
+ return r;
+ }
+
+ return NULL;
+}
+
+static void fwd_ipt_add_srcport(
+ struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
+) {
+ if( p != NULL )
+ {
+ if( p->min < p->max )
+ fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max);
+ else
+ fwd_ipt_rule_append(r, " --sport %u", p->min);
+ }
+}
+
+static void fwd_ipt_add_destport(
+ struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
+) {
+ if( p != NULL )
+ {
+ if( p->min < p->max )
+ fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max);
+ else
+ fwd_ipt_rule_append(r, " --dport %u", p->min);
+ }
+}
+
+static void fwd_ipt_add_proto(
+ struct fwd_ipt_rulebuf *r, struct fwd_proto *p
+) {
+ if( p != NULL )
+ {
+ switch( p->type )
+ {
+ case FWD_PR_TCPUDP:
+ fwd_ipt_rule_append(r, " -p tcp -p udp");
+ break;
+
+ case FWD_PR_TCP:
+ fwd_ipt_rule_append(r, " -p tcp");
+ break;
+
+ case FWD_PR_UDP:
+ fwd_ipt_rule_append(r, " -p udp");
+ break;
+
+ case FWD_PR_ICMP:
+ fwd_ipt_rule_append(r, " -p icmp");
+ break;
+
+ case FWD_PR_ALL:
+ fwd_ipt_rule_append(r, " -p all");
+ break;
+
+ case FWD_PR_CUSTOM:
+ fwd_ipt_rule_append(r, " -p %u", p->proto);
+ break;
+ }
+ }
+}
+
+static void fwd_ipt_add_srcaddr(
+ struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
+) {
+ if( c != NULL )
+ {
+ if( c->prefix < 32 )
+ fwd_ipt_rule_append(r, " -s %s/%u",
+ inet_ntoa(c->addr), c->prefix);
+ else
+ fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr));
+ }
+}
+
+static void fwd_ipt_add_destaddr(
+ struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
+) {
+ if( c != NULL )
+ {
+ if( c->prefix < 32 )
+ fwd_ipt_rule_append(r, " -d %s/%u",
+ inet_ntoa(c->addr), c->prefix);
+ else
+ fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr));
+ }
+}
+
+static void fwd_ipt_add_srcmac(
+ struct fwd_ipt_rulebuf *r, struct fwd_mac *m
+) {
+ if( m != NULL )
+ {
+ fwd_ipt_rule_append(r,
+ " -m mac --mac-source %02x:%02x:%02x:%02x:%02x:%02x",
+ m->mac[0], m->mac[1], m->mac[2],
+ m->mac[3], m->mac[4], m->mac[5]);
+ }
+}
+
+static void fwd_ipt_add_icmptype(
+ struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i
+) {
+ if( i != NULL )
+ {
+ if( i->name )
+ fwd_ipt_rule_append(r, " --icmp-type %s", i->name);
+ else if( i->code > -1 )
+ fwd_ipt_rule_append(r, " --icmp-type %u/%u", i->type, i->code);
+ else
+ fwd_ipt_rule_append(r, " --icmp-type %u", i->type);
+ }
+}
+
+static void fwd_ipt_add_dnat_target(
+ struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p
+) {
+ if( c != NULL )
+ {
+ fwd_ipt_rule_append(r, " -j DNAT --to-destination %s",
+ inet_ntoa(c->addr));
+
+ if( (p != NULL) && (p->min < p->max) )
+ fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max);
+ else if( p != NULL )
+ fwd_ipt_rule_append(r, ":%u", p->min);
+ }
+}
+
+static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r)
+{
+ if( r->len )
+ printf("%s\n", r->buf);
+
+ fwd_free_ptr(r->buf);
+ fwd_free_ptr(r);
+}
+
+static const char * fwd_str_policy(enum fwd_policy pol)
+{
+ return (pol == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
+}
+
+static const char * fwd_str_target(enum fwd_policy pol)
+{
+ switch(pol)
+ {
+ case FWD_P_ACCEPT:
+ return "ACCEPT";
+
+ case FWD_P_REJECT:
+ return "REJECT";
+
+ default:
+ return "DROP";
+ }
+
+ return "DROP";
+}
+
+
+static void fwd_ipt_defaults_create(struct fwd_data *d)
+{
+ struct fwd_defaults *def = &d->section.defaults;
+
+ /* policies */
+ fwd_ipt_exec_format("filter", " -P INPUT %s", fwd_str_policy(def->input));
+ fwd_ipt_exec_format("filter", " -P OUTPUT %s", fwd_str_policy(def->output));
+ fwd_ipt_exec_format("filter", " -P FORWARD %s", fwd_str_policy(def->forward));
+
+ /* invalid state drop */
+ if( def->drop_invalid )
+ {
+ fwd_ipt_exec_format("filter", " -A INPUT --state INVALID -j DROP");
+ fwd_ipt_exec_format("filter", " -A OUTPUT --state INVALID -j DROP");
+ fwd_ipt_exec_format("filter", " -A FORWARD --state INVALID -j DROP");
+ }
+
+ /* default accept related */
+ fwd_ipt_exec_format("filter", " -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT");
+ fwd_ipt_exec_format("filter", " -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT");
+ fwd_ipt_exec_format("filter", " -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT");
+
+ /* default accept on lo */
+ fwd_ipt_exec_format("filter", " -A INPUT -i lo -j ACCEPT");
+ fwd_ipt_exec_format("filter", " -A OUTPUT -o lo -j ACCEPT");
+
+ /* syn flood protection */
+ if( def->syn_flood )
+ {
+ fwd_ipt_exec_format("filter", " -N syn_flood");
+
+ fwd_ipt_exec_format("filter",
+ " -A syn_flood -p tcp --syn -m limit --limit %i/second"
+ " --limit-burst %i -j RETURN",
+ def->syn_rate, def->syn_burst);
+
+ fwd_ipt_exec_format("filter", " -A syn_flood -j DROP");
+ fwd_ipt_exec_format("filter", " -A INPUT -p tcp --syn -j syn_flood");
+ }
+
+ /* standard input/output/forward chain */
+ fwd_ipt_exec_format("filter", " -N input");
+ fwd_ipt_exec_format("filter", " -N output");
+ fwd_ipt_exec_format("filter", " -N forward");
+ fwd_ipt_exec_format("filter", " -A INPUT -j input");
+ fwd_ipt_exec_format("filter", " -A OUTPUT -j output");
+ fwd_ipt_exec_format("filter", " -A FORWARD -j forward");
+
+ /* standard reject chain */
+ fwd_ipt_exec_format("filter", " -N reject");
+ fwd_ipt_exec_format("filter", " -A reject -p tcp -j REJECT --reject-with tcp-reset");
+ fwd_ipt_exec_format("filter", " -A reject -j REJECT --reject-with icmp-port-unreachable");
+}
+
+static void fwd_ipt_zone_create(struct fwd_data *d)
+{
+ struct fwd_zone *z = &d->section.zone;
+
+ if( !strcmp(z->name, "loopback") )
+ return;
+
+ fwd_ipt_exec_format("filter", " -N zone_%s", z->name);
+ fwd_ipt_exec_format("filter", " -N zone_%s_forward", z->name);
+ fwd_ipt_exec_format("filter", " -N zone_%s_ACCEPT", z->name);
+ fwd_ipt_exec_format("filter", " -N zone_%s_REJECT", z->name);
+ fwd_ipt_exec_format("filter", " -N zone_%s_DROP", z->name);
+ fwd_ipt_exec_format("filter", " -N zone_%s_MSSFIX", z->name);
+
+ if( z->forward != FWD_P_UNSPEC )
+ fwd_ipt_exec_format("filter", " -A zone_%s_forward -j zone_%s_%s",
+ z->name, z->name, fwd_str_target(z->forward));
+
+ if( z->input != FWD_P_UNSPEC )
+ fwd_ipt_exec_format("filter", " -A zone_%s -j zone_%s_%s",
+ z->name, z->name, fwd_str_target(z->input));
+
+ if( z->output != FWD_P_UNSPEC )
+ fwd_ipt_exec_format("filter", " -A output -j zone_%s_%s",
+ z->name, fwd_str_target(z->output));
+
+ fwd_ipt_exec_format("nat", " -N zone_%s_nat", z->name);
+ fwd_ipt_exec_format("nat", " -N zone_%s_prerouting", z->name);
+ fwd_ipt_exec_format("raw", " -N zone_%s_notrack", z->name);
+
+ if( z->masq )
+ fwd_ipt_exec_format("nat", " -A POSTROUTING -j zone_%s_nat",
+ z->name);
+
+ if( z->mtu_fix )
+ fwd_ipt_exec_format("filter", " -A FORWARD -j zone_%s_MSSFIX",
+ z->name);
+}
+
+static void fwd_ipt_forwarding_create(struct fwd_data *d)
+{
+ struct fwd_forwarding *f = &d->section.forwarding;
+ struct fwd_ipt_rulebuf *b;
+
+ b = fwd_ipt_init("filter");
+
+ if( f->src )
+ fwd_ipt_add_format(b, " -I zone_%s_forward 1", f->src->name);
+ else
+ fwd_ipt_add_format(b, " -I forward 1");
+
+ if( f->dest )
+ fwd_ipt_add_format(b, " -j zone_%s_ACCEPT", f->dest->name);
+ else
+ fwd_ipt_add_format(b, " -j ACCEPT");
+
+ fwd_ipt_exec(b);
+}
+
+static void fwd_ipt_redirect_create(struct fwd_data *d)
+{
+ struct fwd_redirect *r = &d->section.redirect;
+ struct fwd_ipt_rulebuf *b;
+
+ b = fwd_ipt_init("nat");
+ fwd_ipt_add_format(b, " -A zone_%s_prerouting", r->src->name);
+ fwd_ipt_add_proto(b, r->proto);
+ fwd_ipt_add_srcaddr(b, r->src_ip);
+ fwd_ipt_add_srcport(b, r->src_port);
+ fwd_ipt_add_destport(b, r->src_dport);
+ fwd_ipt_add_srcmac(b, r->src_mac);
+ fwd_ipt_add_dnat_target(b, r->dest_ip, r->dest_port);
+ fwd_ipt_exec(b);
+
+ b = fwd_ipt_init("nat");
+ fwd_ipt_add_format(b, " -I zone_%s_forward 1", r->src->name);
+ fwd_ipt_add_proto(b, r->proto);
+ fwd_ipt_add_srcmac(b, r->src_mac);
+ fwd_ipt_add_srcaddr(b, r->src_ip);
+ fwd_ipt_add_srcport(b, r->src_port);
+ fwd_ipt_add_destaddr(b, r->dest_ip);
+ fwd_ipt_add_destport(b, r->dest_port);
+ fwd_ipt_add_format(b, " -j ACCEPT");
+ fwd_ipt_exec(b);
+}
+
+static void fwd_ipt_rule_create(struct fwd_data *d)
+{
+ struct fwd_rule *r = &d->section.rule;
+ struct fwd_ipt_rulebuf *b;
+
+ b = fwd_ipt_init("filter");
+
+ if( r->dest )
+ fwd_ipt_add_format(b, " -A zone_%s_forward", r->src->name);
+ else
+ fwd_ipt_add_format(b, " -A zone_%s", r->src->name);
+
+ fwd_ipt_add_proto(b, r->proto);
+ fwd_ipt_add_icmptype(b, r->icmp_type);
+ fwd_ipt_add_srcmac(b, r->src_mac);
+ fwd_ipt_add_srcaddr(b, r->src_ip);
+ fwd_ipt_add_srcport(b, r->src_port);
+ fwd_ipt_add_destaddr(b, r->dest_ip);
+ fwd_ipt_add_destport(b, r->dest_port);
+
+ if( r->dest )
+ fwd_ipt_add_format(b, " -j zone_%s_%s",
+ r->dest->name, fwd_str_target(r->target));
+ else
+ fwd_ipt_add_format(b, " -j %s", fwd_str_target(r->target));
+
+ fwd_ipt_exec(b);
+}
+
+
+static struct fwd_network_list *
+fwd_lookup_network(struct fwd_network_list *n, const char *net)
+{
+ struct fwd_network_list *e;
+
+ if( n != NULL )
+ for( e = n; e; e = e->next )
+ if( !strcmp(e->name, net) )
+ return e;
+
+ return NULL;
+}
+
+static struct fwd_addr_list *
+fwd_lookup_addr(struct fwd_addr_list *a, const char *ifname)
+{
+ struct fwd_addr_list *e;
+
+ if( a != NULL )
+ for( e = a; e; e = e->next )
+ if( !strcmp(e->ifname, ifname) )
+ return e;
+
+ return NULL;
+}
+
+
+void fwd_ipt_build_ruleset(struct fwd_handle *h)
+{
+ struct fwd_data *e;
+
+ for( e = h->conf; e; e = e->next )
+ {
+ switch(e->type)
+ {
+ case FWD_S_DEFAULTS:
+ printf("\n## DEFAULTS\n");
+ fwd_ipt_defaults_create(e);
+ break;
+
+ case FWD_S_ZONE:
+ printf("\n## ZONE %s\n", e->section.zone.name);
+ fwd_ipt_zone_create(e);
+ break;
+
+ case FWD_S_FORWARD:
+ printf("\n## FORWARD %s -> %s\n",
+ e->section.forwarding.src
+ ? e->section.forwarding.src->name : "(all)",
+ e->section.forwarding.dest
+ ? e->section.forwarding.dest->name : "(all)");
+ fwd_ipt_forwarding_create(e);
+ break;
+
+ case FWD_S_REDIRECT:
+ printf("\n## REDIRECT %s\n", e->section.forwarding.src->name);
+ fwd_ipt_redirect_create(e);
+ break;
+
+ case FWD_S_RULE:
+ printf("\n## RULE %s\n", e->section.rule.src->name);
+ fwd_ipt_rule_create(e);
+ break;
+
+ case FWD_S_INCLUDE:
+ printf("\n## INCLUDE %s\n", e->section.include.path);
+ break;
+ }
+ }
+}
+
+void fwd_ipt_addif(struct fwd_handle *h, const char *net)
+{
+ struct fwd_data *e;
+ struct fwd_zone *z;
+ struct fwd_addr_list *a;
+ struct fwd_network_list *n;
+
+ for( e = h->conf; e; e = e->next )
+ {
+ if( (e->type != FWD_S_ZONE) ||
+ !(n = fwd_lookup_network(e->section.zone.networks, net)) ||
+ !(a = fwd_lookup_addr(h->addrs, n->ifname)) )
+ continue;
+
+ z = &e->section.zone;
+
+ printf("\n## NETWORK %s (%s - %s/%u)\n",
+ n->name, n->ifname,
+ inet_ntoa(a->ipaddr.v4), a->prefix
+ );
+
+ fwd_ipt_exec_format("filter", " -A input -i %s -j zone_%s",
+ n->ifname, z->name);
+
+ fwd_ipt_exec_format("filter",
+ " -I zone_%s_MSSFIX 1 -o %s -p tcp --tcp-flags SYN,RST SYN"
+ " -j TCPMSS --clamp-mss-to-pmtu",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -o %s -j ACCEPT",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -o %s -j DROP",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -o %s -j reject",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -i %s -j ACCEPT",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -i %s -j DROP",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -i %s -j reject",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter",
+ " -I zone_%s_nat 1 -t nat -o %s -j MASQUERADE",
+ z->name, n->ifname);
+
+ fwd_ipt_exec_format("filter",
+ " -I PREROUTING 1 -t nat -i %s -j zone_%s_prerouting",
+ n->ifname, z->name);
+
+ fwd_ipt_exec_format("filter", " -A forward -i %s -j zone_%s_forward",
+ n->ifname, z->name);
+
+ fwd_ipt_exec_format("raw", " -I PREROUTING 1 -i %s -j zone_%s_notrack",
+ n->ifname, z->name);
+ }
+}
+
diff --git a/contrib/fwd/src/fwd_rules.h b/contrib/fwd/src/fwd_rules.h
new file mode 100644
index 000000000..7074622e3
--- /dev/null
+++ b/contrib/fwd/src/fwd_rules.h
@@ -0,0 +1,44 @@
+/*
+ * fwd - OpenWrt firewall daemon - header for iptables rule set
+ *
+ * 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/.
+ */
+
+#ifndef __FWD_RULES_H__
+#define __FWD_RULES_H__
+
+#include "fwd.h"
+
+#define IPT "iptables"
+
+struct fwd_ipt_rulebuf {
+ char *buf;
+ size_t len;
+};
+
+
+#define fwd_ipt_add_format fwd_ipt_rule_append
+
+#define fwd_ipt_exec_format(t, ...) do { \
+ struct fwd_ipt_rulebuf *r = fwd_ipt_init(t); \
+ fwd_ipt_add_format(r, __VA_ARGS__); \
+ fwd_ipt_exec(r); \
+} while(0)
+
+void fwd_ipt_build_ruleset(struct fwd_handle *h);
+void fwd_ipt_addif(struct fwd_handle *h, const char *net);
+
+#endif
+
diff --git a/contrib/fwd/src/ucix.c b/contrib/fwd/src/ucix.c
new file mode 100644
index 000000000..1d2d87f6d
--- /dev/null
+++ b/contrib/fwd/src/ucix.c
@@ -0,0 +1,236 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <uci_config.h>
+#include <uci.h>
+#include "ucix.h"
+
+static struct uci_ptr ptr;
+
+static inline int ucix_get_ptr(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t)
+{
+ memset(&ptr, 0, sizeof(ptr));
+ ptr.package = p;
+ ptr.section = s;
+ ptr.option = o;
+ ptr.value = t;
+ return uci_lookup_ptr(ctx, &ptr, NULL, true);
+}
+
+struct uci_context* ucix_init(const char *config_file)
+{
+ struct uci_context *ctx = uci_alloc_context();
+ uci_add_history_path(ctx, "/var/state");
+ if(uci_load(ctx, config_file, NULL) != UCI_OK)
+ {
+ printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file);
+ return NULL;
+ }
+ return ctx;
+}
+
+struct uci_context* ucix_init_path(const char *path, const char *config_file)
+{
+ struct uci_context *ctx = uci_alloc_context();
+ if(path)
+ uci_set_confdir(ctx, path);
+ if(uci_load(ctx, config_file, NULL) != UCI_OK)
+ {
+ printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file);
+ return NULL;
+ }
+ return ctx;
+}
+
+int ucix_load(struct uci_context *ctx, const char *config_file)
+{
+ if(uci_load(ctx, config_file, NULL) != UCI_OK)
+ {
+ printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file);
+ return 0;
+ }
+ return 1;
+}
+
+void ucix_cleanup(struct uci_context *ctx)
+{
+ uci_free_context(ctx);
+}
+
+void ucix_save(struct uci_context *ctx)
+{
+ uci_set_savedir(ctx, "/tmp/.uci/");
+ uci_save(ctx, NULL);
+}
+
+void ucix_save_state(struct uci_context *ctx)
+{
+ uci_set_savedir(ctx, "/var/state/");
+ uci_save(ctx, NULL);
+}
+
+const char* ucix_get_option(struct uci_context *ctx, const char *p, const char *s, const char *o)
+{
+ struct uci_element *e = NULL;
+ const char *value = NULL;
+ if(ucix_get_ptr(ctx, p, s, o, NULL))
+ return NULL;
+ if (!(ptr.flags & UCI_LOOKUP_COMPLETE))
+ return NULL;
+ e = ptr.last;
+ switch (e->type)
+ {
+ case UCI_TYPE_SECTION:
+ value = uci_to_section(e)->type;
+ break;
+
+ case UCI_TYPE_OPTION:
+ switch(ptr.o->type) {
+ case UCI_TYPE_STRING:
+ value = ptr.o->v.string;
+ break;
+ default:
+ value = NULL;
+ break;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return value;
+}
+
+int ucix_for_each_list(
+ struct uci_context *ctx, const char *p, const char *s, const char *o,
+ void (*cb)(const char*, void*), void *priv)
+{
+ struct uci_element *e = NULL;
+ char *value = NULL;
+ int count = 0;
+ if(ucix_get_ptr(ctx, p, s, o, NULL))
+ return -1;
+ if (!(ptr.flags & UCI_LOOKUP_COMPLETE))
+ return -1;
+ e = ptr.last;
+ if(e->type == UCI_TYPE_OPTION)
+ {
+ switch(ptr.o->type)
+ {
+ case UCI_TYPE_LIST:
+ uci_foreach_element(&ptr.o->v.list, e) {
+ cb(e->name, priv);
+ count++;
+ }
+ break;
+
+ case UCI_TYPE_STRING:
+ if( (value = strdup(ptr.o->v.string)) != NULL )
+ {
+ char *ts, *tt, *tp;
+ for( ts = value; 1; ts = NULL )
+ {
+ if( (tt = strtok_r(ts, " \t", &tp)) != NULL )
+ {
+ cb(tt, priv);
+ count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ free(value);
+ }
+ break;
+ }
+
+ return count;
+ }
+
+ return -1;
+}
+
+int ucix_get_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int def)
+{
+ const char *tmp = ucix_get_option(ctx, p, s, o);
+ int ret = def;
+
+ if (tmp)
+ ret = atoi(tmp);
+ return ret;
+}
+
+void ucix_add_section(struct uci_context *ctx, const char *p, const char *s, const char *t)
+{
+ if(ucix_get_ptr(ctx, p, s, NULL, t))
+ return;
+ uci_set(ctx, &ptr);
+}
+
+void ucix_add_option(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t)
+{
+ if(ucix_get_ptr(ctx, p, s, o, (t)?(t):("")))
+ return;
+ uci_set(ctx, &ptr);
+}
+
+void ucix_add_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int t)
+{
+ char tmp[64];
+ snprintf(tmp, 64, "%d", t);
+ ucix_add_option(ctx, p, s, o, tmp);
+}
+
+void ucix_del(struct uci_context *ctx, const char *p, const char *s, const char *o)
+{
+ if(!ucix_get_ptr(ctx, p, s, o, NULL))
+ uci_delete(ctx, &ptr);
+}
+
+void ucix_revert(struct uci_context *ctx, const char *p, const char *s, const char *o)
+{
+ if(!ucix_get_ptr(ctx, p, s, o, NULL))
+ uci_revert(ctx, &ptr);
+}
+
+void ucix_for_each_section_type(struct uci_context *ctx,
+ const char *p, const char *t,
+ void (*cb)(struct uci_context *, const char*, void*), void *priv)
+{
+ struct uci_element *e;
+ if(ucix_get_ptr(ctx, p, NULL, NULL, NULL))
+ return;
+ uci_foreach_element(&ptr.p->sections, e)
+ if (!strcmp(t, uci_to_section(e)->type))
+ cb(ctx, e->name, priv);
+}
+
+int ucix_commit(struct uci_context *ctx, const char *p)
+{
+ if(ucix_get_ptr(ctx, p, NULL, NULL, NULL))
+ return 1;
+ return uci_commit(ctx, &ptr.p, false);
+}
+
+
diff --git a/contrib/fwd/src/ucix.h b/contrib/fwd/src/ucix.h
new file mode 100644
index 000000000..d77ac8530
--- /dev/null
+++ b/contrib/fwd/src/ucix.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
+ */
+
+#ifndef __UCIX_H__
+#define __UCIX_H_
+
+struct uci_context* ucix_init(const char *config_file);
+struct uci_context* ucix_init_path(const char *path, const char *config_file);
+int ucix_load(struct uci_context *ctx, const char *config_file);
+void ucix_cleanup(struct uci_context *ctx);
+void ucix_save(struct uci_context *ctx);
+void ucix_save_state(struct uci_context *ctx);
+const char* ucix_get_option(struct uci_context *ctx,
+ const char *p, const char *s, const char *o);
+int ucix_for_each_list(struct uci_context *ctx,
+ const char *p, const char *s, const char *o,
+ void (*cb)(const char*, void*), void *priv);
+int ucix_get_option_int(struct uci_context *ctx,
+ const char *p, const char *s, const char *o, int def);
+void ucix_add_section(struct uci_context *ctx,
+ const char *p, const char *s, const char *t);
+void ucix_add_option(struct uci_context *ctx,
+ const char *p, const char *s, const char *o, const char *t);
+void ucix_add_option_int(struct uci_context *ctx,
+ const char *p, const char *s, const char *o, int t);
+void ucix_for_each_section_type(struct uci_context *ctx,
+ const char *p, const char *t,
+ void (*cb)(struct uci_context *, const char*, void*), void *priv);
+int ucix_commit(struct uci_context *ctx, const char *p);
+void ucix_revert(struct uci_context *ctx,
+ const char *p, const char *s, const char *o);
+void ucix_del(struct uci_context *ctx, const char *p,
+ const char *s, const char *o);
+#endif
+