2 * fwd - OpenWrt firewall daemon - iptables rule set
4 * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
6 * The fwd program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
10 * The fwd program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with the fwd program. If not, see http://www.gnu.org/licenses/.
22 #include "fwd_rules.h"
23 #include "fwd_xtables.h"
26 fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...)
29 char buf[256]; buf[0] = 0;
33 len = vsnprintf(buf, sizeof(buf), fmt, ap);
38 r->buf = realloc(r->buf, r->len + len + 1);
39 memcpy(&r->buf[r->len], buf, len);
40 r->buf[r->len + len] = 0;
45 static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table)
47 struct fwd_ipt_rulebuf *r;
49 if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL )
51 fwd_ipt_rule_append(r, IPT " -t %s", table);
58 static void fwd_ipt_add_srcport(
59 struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
64 fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max);
66 fwd_ipt_rule_append(r, " --sport %u", p->min);
70 static void fwd_ipt_add_destport(
71 struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
76 fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max);
78 fwd_ipt_rule_append(r, " --dport %u", p->min);
82 static void fwd_ipt_add_proto(
83 struct fwd_ipt_rulebuf *r, struct fwd_proto *p
90 fwd_ipt_rule_append(r, " -p tcp -p udp");
94 fwd_ipt_rule_append(r, " -p tcp");
98 fwd_ipt_rule_append(r, " -p udp");
102 fwd_ipt_rule_append(r, " -p icmp");
106 fwd_ipt_rule_append(r, " -p all");
110 fwd_ipt_rule_append(r, " -p %u", p->proto);
116 static void fwd_ipt_add_srcaddr(
117 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
122 fwd_ipt_rule_append(r, " -s %s/%u",
123 inet_ntoa(c->addr), c->prefix);
125 fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr));
129 static void fwd_ipt_add_destaddr(
130 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
135 fwd_ipt_rule_append(r, " -d %s/%u",
136 inet_ntoa(c->addr), c->prefix);
138 fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr));
142 static void fwd_ipt_add_srcmac(
143 struct fwd_ipt_rulebuf *r, struct fwd_mac *m
147 fwd_ipt_rule_append(r,
148 " -m mac --mac-source %02x:%02x:%02x:%02x:%02x:%02x",
149 m->mac[0], m->mac[1], m->mac[2],
150 m->mac[3], m->mac[4], m->mac[5]);
154 static void fwd_ipt_add_icmptype(
155 struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i
160 fwd_ipt_rule_append(r, " --icmp-type %s", i->name);
161 else if( i->code > -1 )
162 fwd_ipt_rule_append(r, " --icmp-type %u/%u", i->type, i->code);
164 fwd_ipt_rule_append(r, " --icmp-type %u", i->type);
168 static void fwd_ipt_add_dnat_target(
169 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p
173 fwd_ipt_rule_append(r, " -j DNAT --to-destination %s",
176 if( (p != NULL) && (p->min < p->max) )
177 fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max);
179 fwd_ipt_rule_append(r, ":%u", p->min);
183 static void fwd_ipt_add_policy_target(
184 struct fwd_ipt_rulebuf *r, enum fwd_policy p
186 fwd_ipt_rule_append(r, " -j %s",
189 : (p == FWD_P_REJECT)
195 static void fwd_ipt_add_comment(
196 struct fwd_ipt_rulebuf *r, const char *t, struct fwd_zone *z,
197 struct fwd_network_list *n, struct fwd_network_list *n2
199 if( (n != NULL) && (n2 != NULL) )
200 fwd_ipt_add_format(r, " -m comment --comment '%s:%s src:%s dest:%s'",
201 t, z->name, n->name, n2->name);
202 else if( (n == NULL) && (n2 != NULL) )
203 fwd_ipt_add_format(r, " -m comment --comment '%s:%s dest:%s'",
204 t, z->name, n2->name);
206 fwd_ipt_add_format(r, " -m comment --comment '%s:%s src:%s'",
207 t, z->name, n->name);
210 static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r)
213 printf("%s\n", r->buf);
215 fwd_free_ptr(r->buf);
219 /* -P <chain> <policy> */
220 static void fwd_r_set_policy(
221 struct iptc_handle *h, const char *chain, const char *policy
223 iptc_set_policy(chain, policy, NULL, h);
227 static void fwd_r_new_chain(struct iptc_handle *h, const char *chain)
229 iptc_create_chain(chain, h);
232 /* -A <chain1> -j <chain2> */
233 static void fwd_r_jump_chain(
234 struct iptc_handle *h, const char *chain1, const char *chain2
236 struct fwd_xt_rule *r;
238 if( (r = fwd_xt_init_rule(h)) != NULL )
240 fwd_xt_get_target(r, chain2);
241 fwd_xt_exec_rule(r, chain1);
245 /* -A <chain> -m state --state INVALID -j DROP */
246 static void fwd_r_drop_invalid(struct iptc_handle *h, const char *chain)
248 struct fwd_xt_rule *r;
249 struct xtables_match *m;
251 if( (r = fwd_xt_init_rule(h)) != NULL )
253 if( (m = fwd_xt_get_match(r, "state")) != NULL )
255 fwd_xt_parse_match(r, m, "--state", "INVALID", 0);
256 fwd_xt_get_target(r, "DROP");
257 fwd_xt_exec_rule(r, chain);
262 /* -A <chain> -m state --state RELATED,ESTABLISHED -j ACCEPT */
263 static void fwd_r_accept_related(struct iptc_handle *h, const char *chain)
265 struct fwd_xt_rule *r;
266 struct xtables_match *m;
268 if( (r = fwd_xt_init_rule(h)) != NULL )
270 if( (m = fwd_xt_get_match(r, "state")) != NULL )
272 fwd_xt_parse_match(r, m, "--state", "RELATED,ESTABLISHED", 0);
273 fwd_xt_get_target(r, "ACCEPT");
274 fwd_xt_exec_rule(r, chain);
279 /* -A INPUT -i lo -j ACCEPT; -A OUTPUT -o lo -j ACCEPT */
280 static void fwd_r_accept_lo(struct iptc_handle *h)
282 struct fwd_network_list n;
283 struct fwd_xt_rule *r;
287 if( (r = fwd_xt_init_rule(h)) != NULL )
289 fwd_xt_parse_in(r, &n, 0);
290 fwd_xt_get_target(r, "ACCEPT");
291 fwd_xt_exec_rule(r, "INPUT");
294 if( (r = fwd_xt_init_rule(h)) != NULL )
296 fwd_xt_parse_out(r, &n, 0);
297 fwd_xt_get_target(r, "ACCEPT");
298 fwd_xt_exec_rule(r, "OUTPUT");
302 /* build syn_flood chain and jump rule */
303 static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
306 struct fwd_xt_rule *r;
307 struct xtables_match *m;
311 fwd_r_new_chain(h, "syn_flood");
314 if( (r = fwd_xt_init_rule(h)) != NULL )
318 fwd_xt_parse_proto(r, &p, 0);
321 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
323 fwd_xt_parse_match(r, m, "--syn", NULL, 0);
326 /* -m limit --limit x/second --limit-burst y */
327 if( (m = fwd_xt_get_match(r, "limit")) != NULL )
329 sprintf(buf, "%i/second", def->syn_rate);
330 fwd_xt_parse_match(r, m, "--limit", buf, 0);
332 sprintf(buf, "%i", def->syn_burst);
333 fwd_xt_parse_match(r, m, "--limit-burst", buf, 0);
336 /* -j RETURN; -A syn_flood */
337 fwd_xt_get_target(r, "RETURN");
338 fwd_xt_exec_rule(r, "syn_flood");
342 if( (r = fwd_xt_init_rule(h)) != NULL )
344 /* -j DROP; -A syn_flood */
345 fwd_xt_get_target(r, "DROP");
346 fwd_xt_exec_rule(r, "syn_flood");
349 /* jump to syn_flood rule */
350 if( (r = fwd_xt_init_rule(h)) != NULL )
354 fwd_xt_parse_proto(r, &p, 0);
357 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
359 fwd_xt_parse_match(r, m, "--syn", NULL, 0);
362 /* -j syn_flood; -A INPUT */
363 fwd_xt_get_target(r, "syn_flood");
364 fwd_xt_exec_rule(r, "INPUT");
368 /* build reject target chain */
369 static void fwd_r_handle_reject(struct iptc_handle *h)
372 struct fwd_xt_rule *r;
373 struct xtables_target *t;
375 /* -N handle_reject */
376 fwd_r_new_chain(h, "handle_reject");
378 /* tcp reject rule */
379 if( (r = fwd_xt_init_rule(h)) != NULL )
383 fwd_xt_parse_proto(r, &p, 0);
385 /* -j REJECT --reject-with tcp-reset */
386 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
388 fwd_xt_parse_target(r, t, "--reject-with", "tcp-reset", 0);
391 /* -A handle_reject */
392 fwd_xt_exec_rule(r, "handle_reject");
395 /* common reject rule */
396 if( (r = fwd_xt_init_rule(h)) != NULL )
398 /* -j REJECT --reject-with icmp-port-unreachable */
399 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
401 fwd_xt_parse_target(r, t, "--reject-with",
402 "icmp-port-unreachable", 0);
405 /* -A handle_reject */
406 fwd_xt_exec_rule(r, "handle_reject");
410 /* build drop target chain */
411 static void fwd_r_handle_drop(struct iptc_handle *h)
413 struct fwd_xt_rule *r;
416 fwd_r_new_chain(h, "handle_drop");
418 /* common drop rule */
419 if( (r = fwd_xt_init_rule(h)) != NULL )
421 /* -j DROP; -A handle_reject */
422 fwd_xt_get_target(r, "DROP");
423 fwd_xt_exec_rule(r, "handle_reject");
427 /* build accept target chain */
428 static void fwd_r_handle_accept(struct iptc_handle *h)
430 struct fwd_xt_rule *r;
432 /* -N handle_accept */
433 fwd_r_new_chain(h, "handle_accept");
435 /* common accept rule */
436 if( (r = fwd_xt_init_rule(h)) != NULL )
438 /* -j ACCEPT; -A handle_accept */
439 fwd_xt_get_target(r, "ACCEPT");
440 fwd_xt_exec_rule(r, "handle_accept");
445 static void fwd_ipt_defaults_create(struct fwd_data *d)
447 struct fwd_defaults *def = &d->section.defaults;
448 struct iptc_handle *h_filter, *h_nat;
450 if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
451 fwd_fatal("Unable to obtain libiptc handle");
454 fwd_r_set_policy(h_filter, "INPUT",
455 def->input == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
456 fwd_r_set_policy(h_filter, "OUTPUT",
457 def->output == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
458 fwd_r_set_policy(h_filter, "FORWARD",
459 def->forward == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
461 /* invalid state drop */
462 if( def->drop_invalid )
464 fwd_r_drop_invalid(h_filter, "INPUT");
465 fwd_r_drop_invalid(h_filter, "OUTPUT");
466 fwd_r_drop_invalid(h_filter, "FORWARD");
469 /* default accept related */
470 fwd_r_accept_related(h_filter, "INPUT");
471 fwd_r_accept_related(h_filter, "OUTPUT");
472 fwd_r_accept_related(h_filter, "FORWARD");
474 /* default accept on lo */
475 fwd_r_accept_lo(h_filter);
477 /* syn flood protection */
480 fwd_r_add_synflood(h_filter, def);
483 /* rule container chains */
484 fwd_r_new_chain(h_filter, "mssfix");
485 fwd_r_new_chain(h_filter, "zones");
486 fwd_r_new_chain(h_filter, "rules");
487 fwd_r_new_chain(h_filter, "redirects");
488 fwd_r_new_chain(h_filter, "forwardings");
489 fwd_r_jump_chain(h_filter, "INPUT", "rules");
490 fwd_r_jump_chain(h_filter, "FORWARD", "mssfix");
491 fwd_r_jump_chain(h_filter, "FORWARD", "zones");
492 fwd_r_jump_chain(h_filter, "FORWARD", "rules");
493 fwd_r_jump_chain(h_filter, "FORWARD", "redirects");
494 fwd_r_jump_chain(h_filter, "FORWARD", "forwardings");
495 fwd_r_new_chain(h_nat, "zonemasq");
496 fwd_r_new_chain(h_nat, "redirects");
497 fwd_r_jump_chain(h_nat, "POSTROUTING", "zonemasq");
498 fwd_r_jump_chain(h_nat, "PREROUTING", "redirects");
499 fwd_r_jump_chain(h_nat, "POSTROUTING", "redirects");
501 /* standard drop, accept, reject chain */
502 fwd_r_handle_drop(h_filter);
503 fwd_r_handle_accept(h_filter);
504 fwd_r_handle_reject(h_filter);
507 if( !iptc_commit(h_nat) )
508 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
510 if( !iptc_commit(h_filter) )
511 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
518 void fwd_ipt_build_ruleset(struct fwd_handle *h)
524 for( e = h->conf; e; e = e->next )
529 printf("\n## DEFAULTS\n");
530 fwd_ipt_defaults_create(e);
534 printf("\n## INCLUDE %s\n", e->section.include.path);
548 static struct fwd_zone *
549 fwd_lookup_zone(struct fwd_handle *h, const char *net)
552 struct fwd_network_list *n;
554 for( e = h->conf; e; e = e->next )
555 if( e->type == FWD_S_ZONE )
556 for( n = e->section.zone.networks; n; n = n->next )
557 if( !strcmp(n->name, net) )
558 return &e->section.zone;
563 static struct fwd_network_list *
564 fwd_lookup_network(struct fwd_zone *z, const char *net)
566 struct fwd_network_list *n;
568 for( n = z->networks; n; n = n->next )
569 if( !strcmp(n->name, net) )
575 static struct fwd_addr_list *
576 fwd_lookup_addr(struct fwd_handle *h, struct fwd_network_list *n)
578 struct fwd_addr_list *a;
581 for( a = h->addrs; a; a = a->next )
582 if( !strcmp(a->ifname, n->ifname) )
588 void fwd_ipt_addif(struct fwd_handle *h, const char *net)
592 struct fwd_ipt_rulebuf *b;
594 struct fwd_redirect *r;
595 struct fwd_forwarding *f;
596 struct fwd_addr_list *a, *a2;
597 struct fwd_network_list *n, *n2;
599 if( !(z = fwd_lookup_zone(h, net)) )
602 if( !(n = fwd_lookup_network(z, net)) )
605 if( !(a = fwd_lookup_addr(h, n)) )
608 printf("\n\n#\n# addif(%s)\n#\n", net);
610 /* Build masquerading rule */
613 printf("\n# Net %s (%s) - masq\n", n->name, n->ifname);
615 b = fwd_ipt_init("nat");
616 fwd_ipt_add_format(b, " -A zonemasq -o %s -j MASQUERADE", n->ifname);
617 fwd_ipt_add_comment(b, "masq", z, NULL, n);
621 /* Build MSS fix rule */
624 printf("\n# Net %s (%s) - mtu_fix\n", n->name, n->ifname);
626 b = fwd_ipt_init("filter");
627 fwd_ipt_add_format(b,
628 " -A mssfix -o %s -p tcp --tcp-flags SYN,RST SYN"
629 " -j TCPMSS --clamp-mss-to-pmtu", n->ifname);
630 fwd_ipt_add_comment(b, "mssfix", z, NULL, n);
634 /* Build intra-zone forwarding rules */
635 for( n2 = z->networks; n2; n2 = n2->next )
637 if( (a2 = fwd_lookup_addr(h, n2)) != NULL )
639 printf("\n# Net %s (%s) - intra-zone-forwarding"
640 " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
641 n->name, n->ifname, z->name, n->name, n->ifname,
642 z->name, n2->name, n2->ifname);
644 b = fwd_ipt_init("filter");
645 fwd_ipt_add_format(b, " -A zones -i %s -o %s",
646 n->ifname, n2->ifname);
647 fwd_ipt_add_policy_target(b, z->forward);
648 fwd_ipt_add_comment(b, "zone", z, n, n2);
653 /* Build inter-zone forwarding rules */
654 for( e = z->forwardings; e && (f = &e->section.forwarding); e = e->next )
656 for( n2 = f->dest->networks; n2; n2 = n2->next )
658 printf("\n# Net %s (%s) - inter-zone-forwarding"
659 " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
660 n->name, n->ifname, z->name, n->name, n->ifname,
661 f->dest->name, n2->name, n2->ifname);
663 /* Build forwarding rule */
664 b = fwd_ipt_init("filter");
665 fwd_ipt_add_format(b, " -A forwardings -i %s -o %s",
666 n->ifname, n2->ifname);
667 fwd_ipt_add_policy_target(b, FWD_P_ACCEPT);
668 fwd_ipt_add_comment(b, "forward", z, n, n2);
673 /* Build DNAT rules */
674 for( e = z->redirects; e && (r = &e->section.redirect); e = e->next )
676 printf("\n# Net %s (%s) - redirect Z:%s N:%s I:%s\n",
677 n->name, n->ifname, z->name, n->name, n->ifname);
680 b = fwd_ipt_init("nat");
681 fwd_ipt_add_format(b, " -A redirects -i %s -d %s",
682 n->ifname, inet_ntoa(a->ipaddr.v4));
683 fwd_ipt_add_proto(b, r->proto);
684 fwd_ipt_add_srcaddr(b, r->src_ip);
685 fwd_ipt_add_srcport(b, r->src_port);
686 fwd_ipt_add_destport(b, r->src_dport);
687 fwd_ipt_add_srcmac(b, r->src_mac);
688 fwd_ipt_add_dnat_target(b, r->dest_ip, r->dest_port);
689 fwd_ipt_add_comment(b, "redir", z, n, NULL);
693 b = fwd_ipt_init("filter");
694 fwd_ipt_add_format(b, " -A redirects -i %s", n->ifname);
695 fwd_ipt_add_proto(b, r->proto);
696 fwd_ipt_add_srcmac(b, r->src_mac);
697 fwd_ipt_add_srcaddr(b, r->src_ip);
698 fwd_ipt_add_srcport(b, r->src_port);
699 fwd_ipt_add_destaddr(b, r->dest_ip);
700 fwd_ipt_add_destport(b, r->dest_port);
701 fwd_ipt_add_policy_target(b, FWD_P_ACCEPT);
702 fwd_ipt_add_comment(b, "redir", z, n, NULL);
705 /* Add loopback rule if neither src_ip nor src_mac are defined */
706 if( !r->src_ip && !r->src_mac )
708 b = fwd_ipt_init("nat");
709 fwd_ipt_add_format(b, " -A redirects -i ! %s -d %s",
710 n->ifname, inet_ntoa(r->dest_ip->addr));
711 fwd_ipt_add_proto(b, r->proto);
712 fwd_ipt_add_srcport(b, r->src_port);
713 fwd_ipt_add_destport(b, r->src_dport);
714 fwd_ipt_add_format(b, " -j MASQUERADE");
715 fwd_ipt_add_comment(b, "redir", z, n, NULL);
721 for( e = z->rules; e && (c = &e->section.rule); e = e->next )
723 /* Has destination, add forward rule for each network in target zone */
726 for( n2 = c->dest->networks; n2; n2 = n2->next )
728 printf("\n# Net %s (%s) - rule+dest"
729 " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
730 n->name, n->ifname, z->name, n->name, n->ifname,
731 f->dest->name, n2->name, n2->ifname);
733 b = fwd_ipt_init("filter");
734 fwd_ipt_add_format(b, " -A rules -i %s -o %s",
735 n->ifname, n2->ifname);
736 fwd_ipt_add_proto(b, c->proto);
737 fwd_ipt_add_icmptype(b, c->icmp_type);
738 fwd_ipt_add_srcmac(b, c->src_mac);
739 fwd_ipt_add_srcaddr(b, c->src_ip);
740 fwd_ipt_add_srcport(b, c->src_port);
741 fwd_ipt_add_destaddr(b, c->dest_ip);
742 fwd_ipt_add_destport(b, c->dest_port);
743 fwd_ipt_add_policy_target(b, c->target);
744 fwd_ipt_add_comment(b, "rule", z, n, n2);
749 /* No destination specified, treat it as input rule */
752 printf("\n# Net %s (%s) - rule Z:%s N:%s I:%s\n",
753 n->name, n->ifname, z->name, n->name, n->ifname);
755 b = fwd_ipt_init("filter");
756 fwd_ipt_add_format(b, " -A rules -i %s", n->ifname);
757 fwd_ipt_add_proto(b, c->proto);
758 fwd_ipt_add_icmptype(b, c->icmp_type);
759 fwd_ipt_add_srcmac(b, c->src_mac);
760 fwd_ipt_add_srcaddr(b, c->src_ip);
761 fwd_ipt_add_srcport(b, c->src_port);
762 fwd_ipt_add_destaddr(b, c->dest_ip);
763 fwd_ipt_add_destport(b, c->dest_port);
764 fwd_ipt_add_policy_target(b, c->target);
765 fwd_ipt_add_comment(b, "rule", z, n, n2);