contrib: fwd - initial C implementation of the uci firewall
authorJo-Philipp Wich <jow@openwrt.org>
Wed, 9 Dec 2009 02:15:59 +0000 (02:15 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Wed, 9 Dec 2009 02:15:59 +0000 (02:15 +0000)
contrib/fwd/src/Makefile [new file with mode: 0644]
contrib/fwd/src/fwd.c [new file with mode: 0644]
contrib/fwd/src/fwd.h [new file with mode: 0644]
contrib/fwd/src/fwd_addr.c [new file with mode: 0644]
contrib/fwd/src/fwd_addr.h [new file with mode: 0644]
contrib/fwd/src/fwd_config.c [new file with mode: 0644]
contrib/fwd/src/fwd_config.h [new file with mode: 0644]
contrib/fwd/src/fwd_rules.c [new file with mode: 0644]
contrib/fwd/src/fwd_rules.h [new file with mode: 0644]
contrib/fwd/src/ucix.c [new file with mode: 0644]
contrib/fwd/src/ucix.h [new file with mode: 0644]

diff --git a/contrib/fwd/src/Makefile b/contrib/fwd/src/Makefile
new file mode 100644 (file)
index 0000000..3a7736d
--- /dev/null
@@ -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 (file)
index 0000000..44b7f5a
--- /dev/null
@@ -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 (file)
index 0000000..c93c0af
--- /dev/null
@@ -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 (file)
index 0000000..df5d8e7
--- /dev/null
@@ -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 (file)
index 0000000..1a28f64
--- /dev/null
@@ -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 (file)
index 0000000..1d16606
--- /dev/null
@@ -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 (file)
index 0000000..ba66dff
--- /dev/null
@@ -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 (file)
index 0000000..9c23341
--- /dev/null
@@ -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 (file)
index 0000000..7074622
--- /dev/null
@@ -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 (file)
index 0000000..1d2d87f
--- /dev/null
@@ -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 (file)
index 0000000..d77ac85
--- /dev/null
@@ -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
+