From 2e9ac3b3420350737aa37d01c0418bede10ab401 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 9 Dec 2009 02:15:59 +0000 Subject: [PATCH] contrib: fwd - initial C implementation of the uci firewall --- contrib/fwd/src/Makefile | 14 + contrib/fwd/src/fwd.c | 57 +++ contrib/fwd/src/fwd.h | 201 ++++++++++ contrib/fwd/src/fwd_addr.c | 150 ++++++++ contrib/fwd/src/fwd_addr.h | 52 +++ contrib/fwd/src/fwd_config.c | 870 +++++++++++++++++++++++++++++++++++++++++++ contrib/fwd/src/fwd_config.h | 29 ++ contrib/fwd/src/fwd_rules.c | 517 +++++++++++++++++++++++++ contrib/fwd/src/fwd_rules.h | 44 +++ contrib/fwd/src/ucix.c | 236 ++++++++++++ contrib/fwd/src/ucix.h | 50 +++ 11 files changed, 2220 insertions(+) create mode 100644 contrib/fwd/src/Makefile create mode 100644 contrib/fwd/src/fwd.c create mode 100644 contrib/fwd/src/fwd.h create mode 100644 contrib/fwd/src/fwd_addr.c create mode 100644 contrib/fwd/src/fwd_addr.h create mode 100644 contrib/fwd/src/fwd_config.c create mode 100644 contrib/fwd/src/fwd_config.h create mode 100644 contrib/fwd/src/fwd_rules.c create mode 100644 contrib/fwd/src/fwd_rules.h create mode 100644 contrib/fwd/src/ucix.c create mode 100644 contrib/fwd/src/ucix.h 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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include + + +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 + * + * 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 + * + * 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 + * + * 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 + */ + +#include +#include +#include + +#include +#include +#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 + */ + +#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 + -- 2.11.0