c3d4ebbe5332fadab9e7f1da00211fb4963dd33e
[project/luci.git] / contrib / fwd / src / fwd_rules.c
1 /*
2  * fwd - OpenWrt firewall daemon - iptables rule set
3  *
4  *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
5  *
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.
9  *
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.
14  *
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/.
17  */
18
19
20 #include "fwd.h"
21 #include "fwd_addr.h"
22 #include "fwd_rules.h"
23 #include "fwd_xtables.h"
24
25 static void
26 fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...)
27 {
28         int len = 0;
29         char buf[256]; buf[0] = 0;
30
31         va_list ap;
32         va_start(ap, fmt);
33         len = vsnprintf(buf, sizeof(buf), fmt, ap);
34         va_end(ap);     
35
36         if( len > 0 )
37         {
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;
41                 r->len += len;
42         }
43 }
44
45 static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table)
46 {
47         struct fwd_ipt_rulebuf *r;
48
49         if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL )
50         {
51                 fwd_ipt_rule_append(r, IPT " -t %s", table);
52                 return r;
53         }
54
55         return NULL;
56 }
57
58 static void fwd_ipt_add_srcport(
59         struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
60 ) {
61         if( p != NULL )
62         {
63                 if( p->min < p->max )
64                         fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max);
65                 else
66                         fwd_ipt_rule_append(r, " --sport %u", p->min);
67         }
68 }
69
70 static void fwd_ipt_add_destport(
71         struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
72 ) {
73         if( p != NULL )
74         {
75                 if( p->min < p->max )
76                         fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max);
77                 else
78                         fwd_ipt_rule_append(r, " --dport %u", p->min);
79         }
80 }
81
82 static void fwd_ipt_add_proto(
83         struct fwd_ipt_rulebuf *r, struct fwd_proto *p
84 ) {
85         if( p != NULL )
86         {
87                 switch( p->type )
88                 {
89                         case FWD_PR_TCPUDP:
90                                 fwd_ipt_rule_append(r, " -p tcp -p udp");
91                                 break;
92
93                         case FWD_PR_TCP:
94                                 fwd_ipt_rule_append(r, " -p tcp");
95                                 break;
96
97                         case FWD_PR_UDP:
98                                 fwd_ipt_rule_append(r, " -p udp");
99                                 break;
100
101                         case FWD_PR_ICMP:
102                                 fwd_ipt_rule_append(r, " -p icmp");
103                                 break;
104
105                         case FWD_PR_ALL:
106                                 fwd_ipt_rule_append(r, " -p all");
107                                 break;
108
109                         case FWD_PR_CUSTOM:
110                                 fwd_ipt_rule_append(r, " -p %u", p->proto);
111                                 break;
112                 }
113         }
114 }
115
116 static void fwd_ipt_add_srcaddr(
117         struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
118 ) {
119         if( c != NULL )
120         {
121                 if( c->prefix < 32 )
122                         fwd_ipt_rule_append(r, " -s %s/%u",
123                                 inet_ntoa(c->addr), c->prefix);
124                 else
125                         fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr));
126         }
127 }
128
129 static void fwd_ipt_add_destaddr(
130         struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
131 ) {
132         if( c != NULL )
133         {
134                 if( c->prefix < 32 )
135                         fwd_ipt_rule_append(r, " -d %s/%u",
136                                 inet_ntoa(c->addr), c->prefix);
137                 else
138                         fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr));
139         }
140 }
141
142 static void fwd_ipt_add_srcmac(
143         struct fwd_ipt_rulebuf *r, struct fwd_mac *m
144 ) {
145         if( m != NULL )
146         {
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]);
151         }
152 }
153
154 static void fwd_ipt_add_icmptype(
155         struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i
156 ) {
157         if( i != NULL )
158         {
159                 if( i->name )
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);
163                 else
164                         fwd_ipt_rule_append(r, " --icmp-type %u", i->type);
165         }
166 }
167
168 static void fwd_ipt_add_dnat_target(
169         struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p
170 ) {
171         if( c != NULL )
172         {
173                 fwd_ipt_rule_append(r, " -j DNAT --to-destination %s",
174                         inet_ntoa(c->addr));
175
176                 if( (p != NULL) && (p->min < p->max) )
177                         fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max);
178                 else if( p != NULL )
179                         fwd_ipt_rule_append(r, ":%u", p->min);
180         }
181 }
182
183 static void fwd_ipt_add_policy_target(
184         struct fwd_ipt_rulebuf *r, enum fwd_policy p
185 ) {
186         fwd_ipt_rule_append(r, " -j %s",
187                 (p == FWD_P_ACCEPT)
188                         ? "handle_accept"
189                         : (p == FWD_P_REJECT)
190                                 ? "handle_reject"
191                                 : "handle_drop"
192         );
193 }
194
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
198 ) {
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);
205         else
206                 fwd_ipt_add_format(r, " -m comment --comment '%s:%s src:%s'",
207                         t, z->name, n->name);
208 }
209
210 static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r)
211 {
212         if( r->len )
213                 printf("%s\n", r->buf);
214
215         fwd_free_ptr(r->buf);
216         fwd_free_ptr(r);
217 }
218
219 /* -P <chain> <policy> */
220 static void fwd_r_set_policy(
221         struct iptc_handle *h, const char *chain, const char *policy
222 ) {
223         iptc_set_policy(chain, policy, NULL, h);
224 }
225
226 /* -N <chain> */
227 static void fwd_r_new_chain(struct iptc_handle *h, const char *chain)
228 {
229         iptc_create_chain(chain, h);
230 }
231
232 /* -A <chain1> -j <chain2> */
233 static void fwd_r_jump_chain(
234         struct iptc_handle *h, const char *chain1, const char *chain2
235 ) {
236         struct fwd_xt_rule *r;
237
238         if( (r = fwd_xt_init_rule(h)) != NULL )
239         {
240                 fwd_xt_get_target(r, chain2);
241                 fwd_xt_exec_rule(r, chain1);
242         }
243 }
244
245 /* -A <chain> -m state --state INVALID -j DROP */
246 static void fwd_r_drop_invalid(struct iptc_handle *h, const char *chain)
247 {
248         struct fwd_xt_rule *r;
249         struct xtables_match *m;
250
251         if( (r = fwd_xt_init_rule(h)) != NULL )
252         {
253                 if( (m = fwd_xt_get_match(r, "state")) != NULL )
254                 {
255                         fwd_xt_parse_match(r, m, "--state", "INVALID", 0);
256                         fwd_xt_get_target(r, "DROP");
257                         fwd_xt_exec_rule(r, chain);
258                 }
259         }
260 }
261
262 /* -A <chain> -m state --state RELATED,ESTABLISHED -j ACCEPT */
263 static void fwd_r_accept_related(struct iptc_handle *h, const char *chain)
264 {
265         struct fwd_xt_rule *r;
266         struct xtables_match *m;
267
268         if( (r = fwd_xt_init_rule(h)) != NULL )
269         {
270                 if( (m = fwd_xt_get_match(r, "state")) != NULL )
271                 {
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);
275                 }
276         }
277 }
278
279 /* -A INPUT -i lo -j ACCEPT; -A OUTPUT -o lo -j ACCEPT */
280 static void fwd_r_accept_lo(struct iptc_handle *h)
281 {
282         struct fwd_network_list n;
283         struct fwd_xt_rule *r;
284
285         n.ifname = "lo";
286
287         if( (r = fwd_xt_init_rule(h)) != NULL )
288         {
289                 fwd_xt_parse_in(r, &n, 0);
290                 fwd_xt_get_target(r, "ACCEPT");
291                 fwd_xt_exec_rule(r, "INPUT");
292         }
293
294         if( (r = fwd_xt_init_rule(h)) != NULL )
295         {
296                 fwd_xt_parse_out(r, &n, 0);
297                 fwd_xt_get_target(r, "ACCEPT");
298                 fwd_xt_exec_rule(r, "OUTPUT");
299         }
300 }
301
302 /* build syn_flood chain and jump rule */
303 static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
304 {
305         struct fwd_proto p;
306         struct fwd_xt_rule *r;
307         struct xtables_match *m;
308         char buf[32];
309
310         /* -N syn_flood */
311         fwd_r_new_chain(h, "syn_flood");
312
313         /* return rule */
314         if( (r = fwd_xt_init_rule(h)) != NULL )
315         {
316                 /* -p tcp */
317                 p.type = FWD_PR_TCP;
318                 fwd_xt_parse_proto(r, &p, 0);
319
320                 /* -m tcp --syn */
321                 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
322                 {
323                         fwd_xt_parse_match(r, m, "--syn", NULL, 0);
324                 }
325
326                 /* -m limit --limit x/second --limit-burst y */
327                 if( (m = fwd_xt_get_match(r, "limit")) != NULL )
328                 {
329                         sprintf(buf, "%i/second", def->syn_rate);
330                         fwd_xt_parse_match(r, m, "--limit", buf, 0);
331
332                         sprintf(buf, "%i", def->syn_burst);
333                         fwd_xt_parse_match(r, m, "--limit-burst", buf, 0);
334                 }
335
336                 /* -j RETURN; -A syn_flood */
337                 fwd_xt_get_target(r, "RETURN");
338                 fwd_xt_exec_rule(r, "syn_flood");
339         }
340
341         /* drop rule */
342         if( (r = fwd_xt_init_rule(h)) != NULL )
343         {       
344                 /* -j DROP; -A syn_flood */
345                 fwd_xt_get_target(r, "DROP");
346                 fwd_xt_exec_rule(r, "syn_flood");
347         }
348
349         /* jump to syn_flood rule */
350         if( (r = fwd_xt_init_rule(h)) != NULL )
351         {       
352                 /* -p tcp */
353                 p.type = FWD_PR_TCP;
354                 fwd_xt_parse_proto(r, &p, 0);
355
356                 /* -m tcp --syn */
357                 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
358                 {
359                         fwd_xt_parse_match(r, m, "--syn", NULL, 0);
360                 }
361
362                 /* -j syn_flood; -A INPUT */
363                 fwd_xt_get_target(r, "syn_flood");
364                 fwd_xt_exec_rule(r, "INPUT");
365         }
366 }
367
368 /* build reject target chain */
369 static void fwd_r_handle_reject(struct iptc_handle *h)
370 {
371         struct fwd_proto p;
372         struct fwd_xt_rule *r;
373         struct xtables_target *t;
374
375         /* -N handle_reject */
376         fwd_r_new_chain(h, "handle_reject");
377
378         /* tcp reject rule */
379         if( (r = fwd_xt_init_rule(h)) != NULL )
380         {
381                 /* -p tcp */
382                 p.type = FWD_PR_TCP;
383                 fwd_xt_parse_proto(r, &p, 0);
384
385                 /* -j REJECT --reject-with tcp-reset */
386                 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
387                 {
388                         fwd_xt_parse_target(r, t, "--reject-with", "tcp-reset", 0);
389                 }
390
391                 /* -A handle_reject */
392                 fwd_xt_exec_rule(r, "handle_reject");
393         }
394
395         /* common reject rule */
396         if( (r = fwd_xt_init_rule(h)) != NULL )
397         {
398                 /* -j REJECT --reject-with icmp-port-unreachable */
399                 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
400                 {
401                         fwd_xt_parse_target(r, t, "--reject-with",
402                                 "icmp-port-unreachable", 0);
403                 }
404
405                 /* -A handle_reject */
406                 fwd_xt_exec_rule(r, "handle_reject");
407         }
408 }
409
410 /* build drop target chain */
411 static void fwd_r_handle_drop(struct iptc_handle *h)
412 {
413         struct fwd_xt_rule *r;
414
415         /* -N handle_drop */
416         fwd_r_new_chain(h, "handle_drop");
417
418         /* common drop rule */
419         if( (r = fwd_xt_init_rule(h)) != NULL )
420         {
421                 /* -j DROP; -A handle_reject */
422                 fwd_xt_get_target(r, "DROP");
423                 fwd_xt_exec_rule(r, "handle_reject");
424         }
425 }
426
427 /* build accept target chain */
428 static void fwd_r_handle_accept(struct iptc_handle *h)
429 {
430         struct fwd_xt_rule *r;
431
432         /* -N handle_accept */
433         fwd_r_new_chain(h, "handle_accept");
434
435         /* common accept rule */
436         if( (r = fwd_xt_init_rule(h)) != NULL )
437         {
438                 /* -j ACCEPT; -A handle_accept */
439                 fwd_xt_get_target(r, "ACCEPT");
440                 fwd_xt_exec_rule(r, "handle_accept");
441         }
442 }
443
444
445 static void fwd_ipt_defaults_create(struct fwd_data *d)
446 {
447         struct fwd_defaults *def = &d->section.defaults;
448         struct iptc_handle *h_filter, *h_nat;
449
450         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
451                 fwd_fatal("Unable to obtain libiptc handle");
452
453         /* policies */
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");
460
461         /* invalid state drop */
462         if( def->drop_invalid )
463         {
464                 fwd_r_drop_invalid(h_filter, "INPUT");
465                 fwd_r_drop_invalid(h_filter, "OUTPUT");
466                 fwd_r_drop_invalid(h_filter, "FORWARD");
467         }
468
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");
473
474         /* default accept on lo */
475         fwd_r_accept_lo(h_filter);
476
477         /* syn flood protection */
478         if( def->syn_flood )
479         {
480                 fwd_r_add_synflood(h_filter, def);
481         }
482
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");
500
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);
505
506
507         if( !iptc_commit(h_nat) )
508                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
509
510         if( !iptc_commit(h_filter) )
511                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
512
513         iptc_free(h_nat);
514         iptc_free(h_filter);
515 }
516
517
518 void fwd_ipt_build_ruleset(struct fwd_handle *h)
519 {
520         struct fwd_data *e;
521
522         fwd_xt_init();
523
524         for( e = h->conf; e; e = e->next )
525         {
526                 switch(e->type)
527                 {
528                         case FWD_S_DEFAULTS:
529                                 printf("\n## DEFAULTS\n");
530                                 fwd_ipt_defaults_create(e);
531                                 break;
532
533                         case FWD_S_INCLUDE:
534                                 printf("\n## INCLUDE %s\n", e->section.include.path);
535                                 break;
536
537                         case FWD_S_ZONE:
538                         case FWD_S_FORWARD:
539                         case FWD_S_REDIRECT:
540                         case FWD_S_RULE:
541                                 /* Make gcc happy */
542                                 break;
543                 }
544         }
545 }
546
547
548 static struct fwd_zone *
549 fwd_lookup_zone(struct fwd_handle *h, const char *net)
550 {
551         struct fwd_data *e;
552         struct fwd_network_list *n;
553
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;
559
560         return NULL;
561 }
562
563 static struct fwd_network_list *
564 fwd_lookup_network(struct fwd_zone *z, const char *net)
565 {
566         struct fwd_network_list *n;
567
568         for( n = z->networks; n; n = n->next )
569                 if( !strcmp(n->name, net) )
570                         return n;
571
572         return NULL;
573 }
574
575 static struct fwd_addr_list *
576 fwd_lookup_addr(struct fwd_handle *h, struct fwd_network_list *n)
577 {
578         struct fwd_addr_list *a;
579
580         if( n != NULL )
581                 for( a = h->addrs; a; a = a->next )
582                         if( !strcmp(a->ifname, n->ifname) )
583                                 return a;
584
585         return NULL;
586 }
587
588 void fwd_ipt_addif(struct fwd_handle *h, const char *net)
589 {
590         struct fwd_data *e;
591         struct fwd_zone *z;
592         struct fwd_ipt_rulebuf *b;
593         struct fwd_rule *c;
594         struct fwd_redirect *r;
595         struct fwd_forwarding *f;
596         struct fwd_addr_list *a, *a2;
597         struct fwd_network_list *n, *n2;
598
599         if( !(z = fwd_lookup_zone(h, net)) )
600                 return;
601
602         if( !(n = fwd_lookup_network(z, net)) )
603                 return;
604
605         if( !(a = fwd_lookup_addr(h, n)) )
606                 return;
607
608         printf("\n\n#\n# addif(%s)\n#\n", net);
609
610         /* Build masquerading rule */
611         if( z->masq )
612         {
613                 printf("\n# Net %s (%s) - masq\n", n->name, n->ifname);
614
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);
618                 fwd_ipt_exec(b);
619         }
620
621         /* Build MSS fix rule */
622         if( z->mtu_fix )
623         {
624                 printf("\n# Net %s (%s) - mtu_fix\n", n->name, n->ifname);
625
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);
631                 fwd_ipt_exec(b);
632         }
633
634         /* Build intra-zone forwarding rules */
635         for( n2 = z->networks; n2; n2 = n2->next )
636         {
637                 if( (a2 = fwd_lookup_addr(h, n2)) != NULL )
638                 {
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);
643
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);
649                         fwd_ipt_exec(b);
650                 }
651         }
652
653         /* Build inter-zone forwarding rules */
654         for( e = z->forwardings; e && (f = &e->section.forwarding); e = e->next )
655         {
656                 for( n2 = f->dest->networks; n2; n2 = n2->next )
657                 {
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);
662
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);
669                         fwd_ipt_exec(b);
670                 }
671         }
672
673         /* Build DNAT rules */
674         for( e = z->redirects; e && (r = &e->section.redirect); e = e->next )
675         {
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);
678
679                 /* DNAT */
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);
690                 fwd_ipt_exec(b);
691
692                 /* Forward */
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);
703                 fwd_ipt_exec(b);
704
705                 /* Add loopback rule if neither src_ip nor src_mac are defined */
706                 if( !r->src_ip && !r->src_mac )
707                 {
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);
716                         fwd_ipt_exec(b);
717                 }
718         }
719
720         /* Build rules */
721         for( e = z->rules; e && (c = &e->section.rule); e = e->next )
722         {
723                 /* Has destination, add forward rule for each network in target zone */
724                 if( c->dest )
725                 {
726                         for( n2 = c->dest->networks; n2; n2 = n2->next )
727                         {
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);
732
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);
745                                 fwd_ipt_exec(b);
746                         }
747                 }
748
749                 /* No destination specified, treat it as input rule */
750                 else
751                 {
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);
754
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);
766                         fwd_ipt_exec(b);
767                 }
768         }
769 }
770