contrib: fwd - initial C implementation of the uci firewall

This commit is contained in:
Jo-Philipp Wich 2009-12-09 02:15:59 +00:00
parent e8220d96a5
commit 2e9ac3b342
11 changed files with 2220 additions and 0 deletions

14
contrib/fwd/src/Makefile Normal file
View file

@ -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

57
contrib/fwd/src/fwd.c Normal file
View file

@ -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;
}

201
contrib/fwd/src/fwd.h Normal file
View file

@ -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

150
contrib/fwd/src/fwd_addr.c Normal file
View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

517
contrib/fwd/src/fwd_rules.c Normal file
View file

@ -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);
}
}

View file

@ -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

236
contrib/fwd/src/ucix.c Normal file
View file

@ -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);
}

50
contrib/fwd/src/ucix.h Normal file
View file

@ -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