diff options
author | Jo-Philipp Wich <jow@openwrt.org> | 2009-12-09 02:15:59 +0000 |
---|---|---|
committer | Jo-Philipp Wich <jow@openwrt.org> | 2009-12-09 02:15:59 +0000 |
commit | 2e9ac3b3420350737aa37d01c0418bede10ab401 (patch) | |
tree | 352ff51ea0379c7709deb28dc8a18318ba47f806 /contrib/fwd/src/fwd_rules.c | |
parent | e8220d96a52be888db8611e2908cb3ba97dfe2f8 (diff) |
contrib: fwd - initial C implementation of the uci firewall
Diffstat (limited to 'contrib/fwd/src/fwd_rules.c')
-rw-r--r-- | contrib/fwd/src/fwd_rules.c | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/contrib/fwd/src/fwd_rules.c b/contrib/fwd/src/fwd_rules.c new file mode 100644 index 000000000..9c233417e --- /dev/null +++ b/contrib/fwd/src/fwd_rules.c @@ -0,0 +1,517 @@ +/* + * fwd - OpenWrt firewall daemon - iptables rule set + * + * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org> + * + * The fwd program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * The fwd program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the fwd program. If not, see http://www.gnu.org/licenses/. + */ + + +#include "fwd.h" +#include "fwd_addr.h" +#include "fwd_rules.h" + +static void +fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...) +{ + int len = 0; + char buf[256]; buf[0] = 0; + + va_list ap; + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if( len > 0 ) + { + r->buf = realloc(r->buf, r->len + len + 1); + memcpy(&r->buf[r->len], buf, len); + r->buf[r->len + len] = 0; + r->len += len; + } +} + +static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table) +{ + struct fwd_ipt_rulebuf *r; + + if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL ) + { + fwd_ipt_rule_append(r, IPT " -t %s", table); + return r; + } + + return NULL; +} + +static void fwd_ipt_add_srcport( + struct fwd_ipt_rulebuf *r, struct fwd_portrange *p +) { + if( p != NULL ) + { + if( p->min < p->max ) + fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max); + else + fwd_ipt_rule_append(r, " --sport %u", p->min); + } +} + +static void fwd_ipt_add_destport( + struct fwd_ipt_rulebuf *r, struct fwd_portrange *p +) { + if( p != NULL ) + { + if( p->min < p->max ) + fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max); + else + fwd_ipt_rule_append(r, " --dport %u", p->min); + } +} + +static void fwd_ipt_add_proto( + struct fwd_ipt_rulebuf *r, struct fwd_proto *p +) { + if( p != NULL ) + { + switch( p->type ) + { + case FWD_PR_TCPUDP: + fwd_ipt_rule_append(r, " -p tcp -p udp"); + break; + + case FWD_PR_TCP: + fwd_ipt_rule_append(r, " -p tcp"); + break; + + case FWD_PR_UDP: + fwd_ipt_rule_append(r, " -p udp"); + break; + + case FWD_PR_ICMP: + fwd_ipt_rule_append(r, " -p icmp"); + break; + + case FWD_PR_ALL: + fwd_ipt_rule_append(r, " -p all"); + break; + + case FWD_PR_CUSTOM: + fwd_ipt_rule_append(r, " -p %u", p->proto); + break; + } + } +} + +static void fwd_ipt_add_srcaddr( + struct fwd_ipt_rulebuf *r, struct fwd_cidr *c +) { + if( c != NULL ) + { + if( c->prefix < 32 ) + fwd_ipt_rule_append(r, " -s %s/%u", + inet_ntoa(c->addr), c->prefix); + else + fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr)); + } +} + +static void fwd_ipt_add_destaddr( + struct fwd_ipt_rulebuf *r, struct fwd_cidr *c +) { + if( c != NULL ) + { + if( c->prefix < 32 ) + fwd_ipt_rule_append(r, " -d %s/%u", + inet_ntoa(c->addr), c->prefix); + else + fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr)); + } +} + +static void fwd_ipt_add_srcmac( + struct fwd_ipt_rulebuf *r, struct fwd_mac *m +) { + if( m != NULL ) + { + fwd_ipt_rule_append(r, + " -m mac --mac-source %02x:%02x:%02x:%02x:%02x:%02x", + m->mac[0], m->mac[1], m->mac[2], + m->mac[3], m->mac[4], m->mac[5]); + } +} + +static void fwd_ipt_add_icmptype( + struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i +) { + if( i != NULL ) + { + if( i->name ) + fwd_ipt_rule_append(r, " --icmp-type %s", i->name); + else if( i->code > -1 ) + fwd_ipt_rule_append(r, " --icmp-type %u/%u", i->type, i->code); + else + fwd_ipt_rule_append(r, " --icmp-type %u", i->type); + } +} + +static void fwd_ipt_add_dnat_target( + struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p +) { + if( c != NULL ) + { + fwd_ipt_rule_append(r, " -j DNAT --to-destination %s", + inet_ntoa(c->addr)); + + if( (p != NULL) && (p->min < p->max) ) + fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max); + else if( p != NULL ) + fwd_ipt_rule_append(r, ":%u", p->min); + } +} + +static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r) +{ + if( r->len ) + printf("%s\n", r->buf); + + fwd_free_ptr(r->buf); + fwd_free_ptr(r); +} + +static const char * fwd_str_policy(enum fwd_policy pol) +{ + return (pol == FWD_P_ACCEPT ? "ACCEPT" : "DROP"); +} + +static const char * fwd_str_target(enum fwd_policy pol) +{ + switch(pol) + { + case FWD_P_ACCEPT: + return "ACCEPT"; + + case FWD_P_REJECT: + return "REJECT"; + + default: + return "DROP"; + } + + return "DROP"; +} + + +static void fwd_ipt_defaults_create(struct fwd_data *d) +{ + struct fwd_defaults *def = &d->section.defaults; + + /* policies */ + fwd_ipt_exec_format("filter", " -P INPUT %s", fwd_str_policy(def->input)); + fwd_ipt_exec_format("filter", " -P OUTPUT %s", fwd_str_policy(def->output)); + fwd_ipt_exec_format("filter", " -P FORWARD %s", fwd_str_policy(def->forward)); + + /* invalid state drop */ + if( def->drop_invalid ) + { + fwd_ipt_exec_format("filter", " -A INPUT --state INVALID -j DROP"); + fwd_ipt_exec_format("filter", " -A OUTPUT --state INVALID -j DROP"); + fwd_ipt_exec_format("filter", " -A FORWARD --state INVALID -j DROP"); + } + + /* default accept related */ + fwd_ipt_exec_format("filter", " -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT"); + fwd_ipt_exec_format("filter", " -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT"); + fwd_ipt_exec_format("filter", " -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT"); + + /* default accept on lo */ + fwd_ipt_exec_format("filter", " -A INPUT -i lo -j ACCEPT"); + fwd_ipt_exec_format("filter", " -A OUTPUT -o lo -j ACCEPT"); + + /* syn flood protection */ + if( def->syn_flood ) + { + fwd_ipt_exec_format("filter", " -N syn_flood"); + + fwd_ipt_exec_format("filter", + " -A syn_flood -p tcp --syn -m limit --limit %i/second" + " --limit-burst %i -j RETURN", + def->syn_rate, def->syn_burst); + + fwd_ipt_exec_format("filter", " -A syn_flood -j DROP"); + fwd_ipt_exec_format("filter", " -A INPUT -p tcp --syn -j syn_flood"); + } + + /* standard input/output/forward chain */ + fwd_ipt_exec_format("filter", " -N input"); + fwd_ipt_exec_format("filter", " -N output"); + fwd_ipt_exec_format("filter", " -N forward"); + fwd_ipt_exec_format("filter", " -A INPUT -j input"); + fwd_ipt_exec_format("filter", " -A OUTPUT -j output"); + fwd_ipt_exec_format("filter", " -A FORWARD -j forward"); + + /* standard reject chain */ + fwd_ipt_exec_format("filter", " -N reject"); + fwd_ipt_exec_format("filter", " -A reject -p tcp -j REJECT --reject-with tcp-reset"); + fwd_ipt_exec_format("filter", " -A reject -j REJECT --reject-with icmp-port-unreachable"); +} + +static void fwd_ipt_zone_create(struct fwd_data *d) +{ + struct fwd_zone *z = &d->section.zone; + + if( !strcmp(z->name, "loopback") ) + return; + + fwd_ipt_exec_format("filter", " -N zone_%s", z->name); + fwd_ipt_exec_format("filter", " -N zone_%s_forward", z->name); + fwd_ipt_exec_format("filter", " -N zone_%s_ACCEPT", z->name); + fwd_ipt_exec_format("filter", " -N zone_%s_REJECT", z->name); + fwd_ipt_exec_format("filter", " -N zone_%s_DROP", z->name); + fwd_ipt_exec_format("filter", " -N zone_%s_MSSFIX", z->name); + + if( z->forward != FWD_P_UNSPEC ) + fwd_ipt_exec_format("filter", " -A zone_%s_forward -j zone_%s_%s", + z->name, z->name, fwd_str_target(z->forward)); + + if( z->input != FWD_P_UNSPEC ) + fwd_ipt_exec_format("filter", " -A zone_%s -j zone_%s_%s", + z->name, z->name, fwd_str_target(z->input)); + + if( z->output != FWD_P_UNSPEC ) + fwd_ipt_exec_format("filter", " -A output -j zone_%s_%s", + z->name, fwd_str_target(z->output)); + + fwd_ipt_exec_format("nat", " -N zone_%s_nat", z->name); + fwd_ipt_exec_format("nat", " -N zone_%s_prerouting", z->name); + fwd_ipt_exec_format("raw", " -N zone_%s_notrack", z->name); + + if( z->masq ) + fwd_ipt_exec_format("nat", " -A POSTROUTING -j zone_%s_nat", + z->name); + + if( z->mtu_fix ) + fwd_ipt_exec_format("filter", " -A FORWARD -j zone_%s_MSSFIX", + z->name); +} + +static void fwd_ipt_forwarding_create(struct fwd_data *d) +{ + struct fwd_forwarding *f = &d->section.forwarding; + struct fwd_ipt_rulebuf *b; + + b = fwd_ipt_init("filter"); + + if( f->src ) + fwd_ipt_add_format(b, " -I zone_%s_forward 1", f->src->name); + else + fwd_ipt_add_format(b, " -I forward 1"); + + if( f->dest ) + fwd_ipt_add_format(b, " -j zone_%s_ACCEPT", f->dest->name); + else + fwd_ipt_add_format(b, " -j ACCEPT"); + + fwd_ipt_exec(b); +} + +static void fwd_ipt_redirect_create(struct fwd_data *d) +{ + struct fwd_redirect *r = &d->section.redirect; + struct fwd_ipt_rulebuf *b; + + b = fwd_ipt_init("nat"); + fwd_ipt_add_format(b, " -A zone_%s_prerouting", r->src->name); + fwd_ipt_add_proto(b, r->proto); + fwd_ipt_add_srcaddr(b, r->src_ip); + fwd_ipt_add_srcport(b, r->src_port); + fwd_ipt_add_destport(b, r->src_dport); + fwd_ipt_add_srcmac(b, r->src_mac); + fwd_ipt_add_dnat_target(b, r->dest_ip, r->dest_port); + fwd_ipt_exec(b); + + b = fwd_ipt_init("nat"); + fwd_ipt_add_format(b, " -I zone_%s_forward 1", r->src->name); + fwd_ipt_add_proto(b, r->proto); + fwd_ipt_add_srcmac(b, r->src_mac); + fwd_ipt_add_srcaddr(b, r->src_ip); + fwd_ipt_add_srcport(b, r->src_port); + fwd_ipt_add_destaddr(b, r->dest_ip); + fwd_ipt_add_destport(b, r->dest_port); + fwd_ipt_add_format(b, " -j ACCEPT"); + fwd_ipt_exec(b); +} + +static void fwd_ipt_rule_create(struct fwd_data *d) +{ + struct fwd_rule *r = &d->section.rule; + struct fwd_ipt_rulebuf *b; + + b = fwd_ipt_init("filter"); + + if( r->dest ) + fwd_ipt_add_format(b, " -A zone_%s_forward", r->src->name); + else + fwd_ipt_add_format(b, " -A zone_%s", r->src->name); + + fwd_ipt_add_proto(b, r->proto); + fwd_ipt_add_icmptype(b, r->icmp_type); + fwd_ipt_add_srcmac(b, r->src_mac); + fwd_ipt_add_srcaddr(b, r->src_ip); + fwd_ipt_add_srcport(b, r->src_port); + fwd_ipt_add_destaddr(b, r->dest_ip); + fwd_ipt_add_destport(b, r->dest_port); + + if( r->dest ) + fwd_ipt_add_format(b, " -j zone_%s_%s", + r->dest->name, fwd_str_target(r->target)); + else + fwd_ipt_add_format(b, " -j %s", fwd_str_target(r->target)); + + fwd_ipt_exec(b); +} + + +static struct fwd_network_list * +fwd_lookup_network(struct fwd_network_list *n, const char *net) +{ + struct fwd_network_list *e; + + if( n != NULL ) + for( e = n; e; e = e->next ) + if( !strcmp(e->name, net) ) + return e; + + return NULL; +} + +static struct fwd_addr_list * +fwd_lookup_addr(struct fwd_addr_list *a, const char *ifname) +{ + struct fwd_addr_list *e; + + if( a != NULL ) + for( e = a; e; e = e->next ) + if( !strcmp(e->ifname, ifname) ) + return e; + + return NULL; +} + + +void fwd_ipt_build_ruleset(struct fwd_handle *h) +{ + struct fwd_data *e; + + for( e = h->conf; e; e = e->next ) + { + switch(e->type) + { + case FWD_S_DEFAULTS: + printf("\n## DEFAULTS\n"); + fwd_ipt_defaults_create(e); + break; + + case FWD_S_ZONE: + printf("\n## ZONE %s\n", e->section.zone.name); + fwd_ipt_zone_create(e); + break; + + case FWD_S_FORWARD: + printf("\n## FORWARD %s -> %s\n", + e->section.forwarding.src + ? e->section.forwarding.src->name : "(all)", + e->section.forwarding.dest + ? e->section.forwarding.dest->name : "(all)"); + fwd_ipt_forwarding_create(e); + break; + + case FWD_S_REDIRECT: + printf("\n## REDIRECT %s\n", e->section.forwarding.src->name); + fwd_ipt_redirect_create(e); + break; + + case FWD_S_RULE: + printf("\n## RULE %s\n", e->section.rule.src->name); + fwd_ipt_rule_create(e); + break; + + case FWD_S_INCLUDE: + printf("\n## INCLUDE %s\n", e->section.include.path); + break; + } + } +} + +void fwd_ipt_addif(struct fwd_handle *h, const char *net) +{ + struct fwd_data *e; + struct fwd_zone *z; + struct fwd_addr_list *a; + struct fwd_network_list *n; + + for( e = h->conf; e; e = e->next ) + { + if( (e->type != FWD_S_ZONE) || + !(n = fwd_lookup_network(e->section.zone.networks, net)) || + !(a = fwd_lookup_addr(h->addrs, n->ifname)) ) + continue; + + z = &e->section.zone; + + printf("\n## NETWORK %s (%s - %s/%u)\n", + n->name, n->ifname, + inet_ntoa(a->ipaddr.v4), a->prefix + ); + + fwd_ipt_exec_format("filter", " -A input -i %s -j zone_%s", + n->ifname, z->name); + + fwd_ipt_exec_format("filter", + " -I zone_%s_MSSFIX 1 -o %s -p tcp --tcp-flags SYN,RST SYN" + " -j TCPMSS --clamp-mss-to-pmtu", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -o %s -j ACCEPT", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -o %s -j DROP", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -o %s -j reject", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -i %s -j ACCEPT", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -i %s -j DROP", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -i %s -j reject", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", + " -I zone_%s_nat 1 -t nat -o %s -j MASQUERADE", + z->name, n->ifname); + + fwd_ipt_exec_format("filter", + " -I PREROUTING 1 -t nat -i %s -j zone_%s_prerouting", + n->ifname, z->name); + + fwd_ipt_exec_format("filter", " -A forward -i %s -j zone_%s_forward", + n->ifname, z->name); + + fwd_ipt_exec_format("raw", " -I PREROUTING 1 -i %s -j zone_%s_notrack", + n->ifname, z->name); + } +} + |