3e7314f36285cc50e44e547693302dd81697206a
[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 #include "fwd_utils.h"
25
26
27 /* -P <chain> <policy> */
28 static void fwd_r_set_policy(
29         struct iptc_handle *h, const char *chain, const char *policy
30 ) {
31         iptc_set_policy(chain, policy, NULL, h);
32 }
33
34 /* -N <chain> */
35 static void fwd_r_new_chain(struct iptc_handle *h, const char *chain)
36 {
37         iptc_create_chain(chain, h);
38 }
39
40 /* -A <chain1> -j <chain2> */
41 static void fwd_r_jump_chain(
42         struct iptc_handle *h, const char *chain1, const char *chain2
43 ) {
44         struct fwd_xt_rule *r;
45
46         if( (r = fwd_xt_init_rule(h)) != NULL )
47         {
48                 fwd_xt_get_target(r, chain2);
49                 fwd_xt_append_rule(r, chain1);
50         }
51 }
52
53 /* -A <chain> -m state --state INVALID -j DROP */
54 static void fwd_r_drop_invalid(struct iptc_handle *h, const char *chain)
55 {
56         struct fwd_xt_rule *r;
57         struct xtables_match *m;
58
59         if( (r = fwd_xt_init_rule(h)) != NULL )
60         {
61                 if( (m = fwd_xt_get_match(r, "state")) != NULL )
62                 {
63                         fwd_xt_parse_match(r, m, "--state", "INVALID");
64                         fwd_xt_get_target(r, "DROP");
65                         fwd_xt_append_rule(r, chain);
66                 }
67         }
68 }
69
70 /* -A <chain> -m state --state RELATED,ESTABLISHED -j ACCEPT */
71 static void fwd_r_accept_related(struct iptc_handle *h, const char *chain)
72 {
73         struct fwd_xt_rule *r;
74         struct xtables_match *m;
75
76         if( (r = fwd_xt_init_rule(h)) != NULL )
77         {
78                 if( (m = fwd_xt_get_match(r, "state")) != NULL )
79                 {
80                         fwd_xt_parse_match(r, m, "--state", "RELATED,ESTABLISHED");
81                         fwd_xt_get_target(r, "ACCEPT");
82                         fwd_xt_append_rule(r, chain);
83                 }
84         }
85 }
86
87 /* -A INPUT -i lo -j ACCEPT; -A OUTPUT -o lo -j ACCEPT */
88 static void fwd_r_accept_lo(struct iptc_handle *h)
89 {
90         struct fwd_network n;
91         struct fwd_xt_rule *r;
92
93         n.ifname = "lo";
94
95         if( (r = fwd_xt_init_rule(h)) != NULL )
96         {
97                 fwd_xt_parse_in(r, &n, 0);
98                 fwd_xt_get_target(r, "ACCEPT");
99                 fwd_xt_append_rule(r, "INPUT");
100         }
101
102         if( (r = fwd_xt_init_rule(h)) != NULL )
103         {
104                 fwd_xt_parse_out(r, &n, 0);
105                 fwd_xt_get_target(r, "ACCEPT");
106                 fwd_xt_append_rule(r, "OUTPUT");
107         }
108 }
109
110 /* build syn_flood chain and jump rule */
111 static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
112 {
113         struct fwd_proto p;
114         struct fwd_xt_rule *r;
115         struct xtables_match *m;
116         char buf[32];
117
118         /* -N syn_flood */
119         fwd_r_new_chain(h, "syn_flood");
120
121         /* return rule */
122         if( (r = fwd_xt_init_rule(h)) != NULL )
123         {
124                 /* -p tcp */
125                 p.type = FWD_PR_TCP;
126                 fwd_xt_parse_proto(r, &p, 0);
127
128                 /* -m tcp --syn */
129                 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
130                 {
131                         fwd_xt_parse_match(r, m, "--syn");
132                 }
133
134                 /* -m limit --limit x/second --limit-burst y */
135                 if( (m = fwd_xt_get_match(r, "limit")) != NULL )
136                 {
137                         sprintf(buf, "%i/second", def->syn_rate);
138                         fwd_xt_parse_match(r, m, "--limit", buf);
139
140                         sprintf(buf, "%i", def->syn_burst);
141                         fwd_xt_parse_match(r, m, "--limit-burst", buf);
142                 }
143
144                 /* -j RETURN; -A syn_flood */
145                 fwd_xt_get_target(r, "RETURN");
146                 fwd_xt_append_rule(r, "syn_flood");
147         }
148
149         /* drop rule */
150         if( (r = fwd_xt_init_rule(h)) != NULL )
151         {       
152                 /* -j DROP; -A syn_flood */
153                 fwd_xt_get_target(r, "DROP");
154                 fwd_xt_append_rule(r, "syn_flood");
155         }
156
157         /* jump to syn_flood rule */
158         if( (r = fwd_xt_init_rule(h)) != NULL )
159         {       
160                 /* -p tcp */
161                 p.type = FWD_PR_TCP;
162                 fwd_xt_parse_proto(r, &p, 0);
163
164                 /* -m tcp --syn */
165                 if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
166                 {
167                         fwd_xt_parse_match(r, m, "--syn");
168                 }
169
170                 /* -j syn_flood; -A INPUT */
171                 fwd_xt_get_target(r, "syn_flood");
172                 fwd_xt_append_rule(r, "INPUT");
173         }
174 }
175
176 /* build reject target chain */
177 static void fwd_r_handle_reject(struct iptc_handle *h)
178 {
179         struct fwd_proto p;
180         struct fwd_xt_rule *r;
181         struct xtables_target *t;
182
183         /* -N handle_reject */
184         fwd_r_new_chain(h, "handle_reject");
185
186         /* tcp reject rule */
187         if( (r = fwd_xt_init_rule(h)) != NULL )
188         {
189                 /* -p tcp */
190                 p.type = FWD_PR_TCP;
191                 fwd_xt_parse_proto(r, &p, 0);
192
193                 /* -j REJECT --reject-with tcp-reset */
194                 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
195                 {
196                         fwd_xt_parse_target(r, t, "--reject-with", "tcp-reset");
197                 }
198
199                 /* -A handle_reject */
200                 fwd_xt_append_rule(r, "handle_reject");
201         }
202
203         /* common reject rule */
204         if( (r = fwd_xt_init_rule(h)) != NULL )
205         {
206                 /* -j REJECT --reject-with icmp-port-unreachable */
207                 if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
208                 {
209                         fwd_xt_parse_target(r, t, "--reject-with",
210                                 "icmp-port-unreachable");
211                 }
212
213                 /* -A handle_reject */
214                 fwd_xt_append_rule(r, "handle_reject");
215         }
216 }
217
218 /* build drop target chain */
219 static void fwd_r_handle_drop(struct iptc_handle *h)
220 {
221         struct fwd_xt_rule *r;
222
223         /* -N handle_drop */
224         fwd_r_new_chain(h, "handle_drop");
225
226         /* common drop rule */
227         if( (r = fwd_xt_init_rule(h)) != NULL )
228         {
229                 /* -j DROP; -A handle_drop */
230                 fwd_xt_get_target(r, "DROP");
231                 fwd_xt_append_rule(r, "handle_drop");
232         }
233 }
234
235 /* build accept target chain */
236 static void fwd_r_handle_accept(struct iptc_handle *h)
237 {
238         struct fwd_xt_rule *r;
239
240         /* -N handle_accept */
241         fwd_r_new_chain(h, "handle_accept");
242
243         /* common accept rule */
244         if( (r = fwd_xt_init_rule(h)) != NULL )
245         {
246                 /* -j ACCEPT; -A handle_accept */
247                 fwd_xt_get_target(r, "ACCEPT");
248                 fwd_xt_append_rule(r, "handle_accept");
249         }
250 }
251
252 /* add comment match */
253 static void fwd_r_add_comment(
254         struct fwd_xt_rule *r, const char *t, struct fwd_zone *z,
255         struct fwd_network *n
256 ) {
257         struct xtables_match *m;
258         char buf[256];
259
260         if( (m = fwd_xt_get_match(r, "comment")) != NULL )
261         {
262                 snprintf(buf, sizeof(buf), "%s:net=%s zone=%s", t, n->name, z->name);
263                 fwd_xt_parse_match(r, m, "--comment", buf);
264         }
265 }
266
267 /* add --sport (if applicable) */
268 static void fwd_r_add_sport(
269         struct fwd_xt_rule *r, struct fwd_portrange *p
270 ) {
271         int proto = r->entry->ip.proto;
272         char buf[12];
273         struct xtables_match *m;
274
275         /* have portrange and proto is tcp or udp ... */
276         if( (p != NULL) && ((proto == 6) || (proto == 17)) )
277         {
278                 /* get match ... */
279                 if( (m = fwd_xt_get_match(r, (proto == 6) ? "tcp" : "udp")) != NULL )
280                 {
281                         snprintf(buf, sizeof(buf), "%u:%u", p->min, p->max);
282                         fwd_xt_parse_match(r, m, "--sport", buf);
283                 }
284         }
285 }
286
287 /* add --dport (if applicable) */
288 static void fwd_r_add_dport(
289         struct fwd_xt_rule *r, struct fwd_portrange *p
290 ) {
291         int proto = r->entry->ip.proto;
292         char buf[12];
293         struct xtables_match *m;
294
295         /* have portrange and proto is tcp or udp ... */
296         if( (p != NULL) && ((proto == 6) || (proto == 17)) )
297         {
298                 /* get match ... */
299                 if( (m = fwd_xt_get_match(r, (proto == 6) ? "tcp" : "udp")) != NULL )
300                 {
301                         snprintf(buf, sizeof(buf), "%u:%u", p->min, p->max);
302                         fwd_xt_parse_match(r, m, "--dport", buf);
303                 }
304         }
305 }
306
307 /* add --icmp-type (of applicable) */
308 static void fwd_r_add_icmptype(
309         struct fwd_xt_rule *r, struct fwd_icmptype *i
310 ) {
311         int proto = r->entry->ip.proto;
312         struct xtables_match *m;
313         char buf[32];
314
315         /* have icmp-type and proto is icmp ... */
316         if( (i != NULL) && (proto == 1) )
317         {
318                 /* get match ... */
319                 if( (m = fwd_xt_get_match(r, "icmp")) != NULL )
320                 {
321                         if( i->name[0] )
322                                 snprintf(buf, sizeof(buf), "%s", i->name);
323                         else
324                                 snprintf(buf, sizeof(buf), "%u/%u", i->type, i->code);
325
326                         fwd_xt_parse_match(r, m, "--icmp-type", buf);
327                 }
328         }
329 }
330
331 /* add -m mac --mac-source ... */
332 static void fwd_r_add_srcmac(
333         struct fwd_xt_rule *r, struct fwd_mac *mac
334 ) {
335         struct xtables_match *m;
336         char buf[18];
337
338         if( mac != NULL )
339         {
340                 if( (m = fwd_xt_get_match(r, "mac")) != NULL )
341                 {
342                         snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
343                                 mac->mac[0], mac->mac[1], mac->mac[2],
344                                 mac->mac[3], mac->mac[4], mac->mac[5]);
345
346                         fwd_xt_parse_match(r, m, "--mac-source", buf);
347                 }
348         }
349 }
350
351 /* add policy target */
352 static void fwd_r_add_policytarget(
353         struct fwd_xt_rule *r, enum fwd_policy pol
354 ) {
355         switch(pol)
356         {
357                 case FWD_P_ACCEPT:
358                         fwd_xt_get_target(r, "handle_accept");
359                         break;
360
361                 case FWD_P_REJECT:
362                         fwd_xt_get_target(r, "handle_reject");
363                         break;
364
365                 case FWD_P_DROP:
366                 case FWD_P_UNSPEC:
367                         fwd_xt_get_target(r, "handle_drop");
368                         break;
369         }
370 }
371
372 /* add dnat target */
373 static void fwd_r_add_dnattarget(
374         struct fwd_xt_rule *r, struct fwd_cidr *c, struct fwd_portrange *p
375 ) {
376         struct xtables_target *t;
377         char buf[32];
378
379         if( c != NULL )
380         {
381                 if( (t = fwd_xt_get_target(r, "DNAT")) != NULL )
382                 {
383                         if( p != NULL )
384                                 snprintf(buf, sizeof(buf), "%s:%u-%u",
385                                         inet_ntoa(c->addr), p->min, p->max);
386                         else
387                                 snprintf(buf, sizeof(buf), "%s", inet_ntoa(c->addr));
388
389                         fwd_xt_parse_target(r, t, "--to-destination", buf);
390                 }
391         }
392 }
393
394 /* parse comment string and look for match */
395 static int fwd_r_cmp(const char *what, const char *cmt, const char *cmp)
396 {
397         char *match;
398
399         if( (match = strstr(cmt, what)) == NULL )
400                 return 0;
401
402         match += strlen(what);
403
404         if( strncmp(match, cmp, strlen(cmp)) != 0 )
405                 return 0;
406
407         if( (match[strlen(cmp)] != ' ') && (match[strlen(cmp)] != '\0') )
408                 return 0;
409
410         return 1;
411 }
412
413
414 static void fwd_ipt_defaults_create(struct fwd_data *d)
415 {
416         struct fwd_defaults *def = &d->section.defaults;
417         struct iptc_handle *h_filter, *h_nat;
418
419         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
420                 fwd_fatal("Unable to obtain libiptc handle");
421
422         /* policies */
423         fwd_r_set_policy(h_filter, "INPUT",
424                 def->input == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
425         fwd_r_set_policy(h_filter, "OUTPUT",
426                 def->output == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
427         fwd_r_set_policy(h_filter, "FORWARD",
428                 def->forward == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
429
430         /* invalid state drop */
431         if( def->drop_invalid )
432         {
433                 fwd_r_drop_invalid(h_filter, "INPUT");
434                 fwd_r_drop_invalid(h_filter, "OUTPUT");
435                 fwd_r_drop_invalid(h_filter, "FORWARD");
436         }
437
438         /* default accept related */
439         fwd_r_accept_related(h_filter, "INPUT");
440         fwd_r_accept_related(h_filter, "OUTPUT");
441         fwd_r_accept_related(h_filter, "FORWARD");
442
443         /* default accept on lo */
444         fwd_r_accept_lo(h_filter);
445
446         /* syn flood protection */
447         if( def->syn_flood )
448         {
449                 fwd_r_add_synflood(h_filter, def);
450         }
451
452         /* rule container chains */
453         fwd_r_new_chain(h_filter, "mssfix");
454         fwd_r_new_chain(h_filter, "zones");
455         fwd_r_new_chain(h_filter, "rules");
456         fwd_r_new_chain(h_filter, "redirects");
457         fwd_r_new_chain(h_filter, "forwardings");
458         fwd_r_jump_chain(h_filter, "INPUT", "rules");
459         fwd_r_jump_chain(h_filter, "FORWARD", "mssfix");
460         fwd_r_jump_chain(h_filter, "FORWARD", "zones");
461         fwd_r_jump_chain(h_filter, "FORWARD", "rules");
462         fwd_r_jump_chain(h_filter, "FORWARD", "redirects");
463         fwd_r_jump_chain(h_filter, "FORWARD", "forwardings");
464         fwd_r_new_chain(h_nat, "zonemasq");
465         fwd_r_new_chain(h_nat, "redirects");
466         fwd_r_new_chain(h_nat, "loopback");
467         fwd_r_jump_chain(h_nat, "POSTROUTING", "zonemasq");
468         fwd_r_jump_chain(h_nat, "PREROUTING", "redirects");
469         fwd_r_jump_chain(h_nat, "POSTROUTING", "loopback");
470
471         /* standard drop, accept, reject chain */
472         fwd_r_handle_drop(h_filter);
473         fwd_r_handle_accept(h_filter);
474         fwd_r_handle_reject(h_filter);
475
476
477         if( !iptc_commit(h_nat) )
478                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
479
480         if( !iptc_commit(h_filter) )
481                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
482
483         iptc_free(h_nat);
484         iptc_free(h_filter);
485 }
486
487
488 void fwd_ipt_build_ruleset(struct fwd_handle *h)
489 {
490         struct fwd_data *e;
491
492         fwd_xt_init();
493
494         for( e = h->conf; e; e = e->next )
495         {
496                 switch(e->type)
497                 {
498                         case FWD_S_DEFAULTS:
499                                 printf("\n## DEFAULTS\n");
500                                 fwd_ipt_defaults_create(e);
501                                 break;
502
503                         case FWD_S_INCLUDE:
504                                 printf("\n## INCLUDE %s\n", e->section.include.path);
505                                 break;
506
507                         case FWD_S_ZONE:
508                         case FWD_S_FORWARD:
509                         case FWD_S_REDIRECT:
510                         case FWD_S_RULE:
511                                 /* Make gcc happy */
512                                 break;
513                 }
514         }
515 }
516
517
518 static struct fwd_zone *
519 fwd_lookup_zone(struct fwd_handle *h, const char *net)
520 {
521         struct fwd_data *e;
522         struct fwd_network *n;
523
524         for( e = h->conf; e; e = e->next )
525                 if( e->type == FWD_S_ZONE )
526                         for( n = e->section.zone.networks; n; n = n->next )
527                                 if( !strcmp(n->name, net) )
528                                         return &e->section.zone;
529
530         return NULL;
531 }
532
533 static struct fwd_network *
534 fwd_lookup_network(struct fwd_zone *z, const char *net)
535 {
536         struct fwd_network *n;
537
538         for( n = z->networks; n; n = n->next )
539                 if( !strcmp(n->name, net) )
540                         return n;
541
542         return NULL;
543 }
544
545 void fwd_ipt_addif(struct fwd_handle *h, const char *net)
546 {
547         struct fwd_data *e;
548         struct fwd_zone *z;
549         struct fwd_rule *c;
550         struct fwd_redirect *r;
551         struct fwd_forwarding *f;
552         struct fwd_cidr *a, *a2;
553         struct fwd_network *n, *n2;
554         struct fwd_proto p;
555
556         struct fwd_xt_rule *x;
557         struct xtables_match *m;
558         struct xtables_target *t;
559
560         struct iptc_handle *h_filter, *h_nat;
561
562         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
563                 fwd_fatal("Unable to obtain libiptc handle");
564
565
566         if( !(z = fwd_lookup_zone(h, net)) )
567                 return;
568
569         if( !(n = fwd_lookup_network(z, net)) )
570                 return;
571
572         if( !(a = n->addr) || fwd_empty_cidr(a) )
573                 return;
574
575
576         printf("\n\n#\n# addif(%s)\n#\n", net);
577
578         /* Build masquerading rule */
579         if( z->masq )
580         {
581                 printf("\n# Net %s (%s) - masq\n", n->name, n->ifname);
582
583                 if( (x = fwd_xt_init_rule(h_nat)) != NULL )
584                 {
585                         fwd_xt_parse_out(x, n, 0);                              /* -o ... */
586                         fwd_xt_get_target(x, "MASQUERADE");             /* -j MASQUERADE */
587                         fwd_r_add_comment(x, "masq", z, n);             /* -m comment ... */
588                         fwd_xt_append_rule(x, "zonemasq");              /* -A zonemasq */
589                 }
590         }
591
592         /* Build MSS fix rule */
593         if( z->mtu_fix )
594         {
595                 printf("\n# Net %s (%s) - mtu_fix\n", n->name, n->ifname);
596
597                 if( (x = fwd_xt_init_rule(h_filter)) != NULL )
598                 {
599                         p.type = FWD_PR_TCP;
600                         fwd_xt_parse_out(x, n, 0);                                      /* -o ... */
601                         fwd_xt_parse_proto(x, &p, 0);                           /* -p tcp */
602
603                         /* -m tcp --tcp-flags SYN,RST SYN */
604                         if( (m = fwd_xt_get_match(x, "tcp")) != NULL )
605                                 fwd_xt_parse_match(x, m, "--tcp-flags", "SYN,RST", "SYN");
606
607                         /* -j TCPMSS --clamp-mss-to-pmtu */
608                         if( (t = fwd_xt_get_target(x, "TCPMSS")) != NULL )
609                                 fwd_xt_parse_target(x, t, "--clamp-mss-to-pmtu");
610
611                         /* -m comment ... */
612                         fwd_r_add_comment(x, "mssfix", z, n);
613
614                         /* -A mssfix */
615                         fwd_xt_append_rule(x, "mssfix");
616                 }
617         }
618
619         /* Build intra-zone forwarding rules */
620         for( n2 = z->networks; n2; n2 = n2->next )
621         {
622                 if( (a2 = n2->addr) != NULL )
623                 {
624                         printf("\n# Net %s (%s) - intra-zone-forwarding"
625                                " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
626                                 n->name, n->ifname, z->name, n->name, n->ifname,
627                                 z->name, n2->name, n2->ifname);
628
629                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
630                         {
631                                 fwd_xt_parse_in(x, n, 0);                               /* -i ... */
632                                 fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
633                                 fwd_r_add_policytarget(x, z->forward);  /* -j handle_... */
634                                 fwd_r_add_comment(x, "zone", z, n);             /* -m comment ... */
635                                 fwd_xt_append_rule(x, "zones");                 /* -A zones */
636                         }
637                 }
638         }
639
640         /* Build inter-zone forwarding rules */
641         for( e = z->forwardings; e && (f = &e->section.forwarding); e = e->next )
642         {
643                 for( n2 = f->dest->networks; n2; n2 = n2->next )
644                 {
645                         printf("\n# Net %s (%s) - inter-zone-forwarding"
646                    " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
647                                 n->name, n->ifname, z->name, n->name, n->ifname,
648                                 f->dest->name, n2->name, n2->ifname);
649
650                         /* Build forwarding rule */
651                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
652                         {
653                                 fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
654                                 fwd_xt_parse_out(x, n2, 0);                                     /* -o ... */
655                                 fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_... */
656                                 fwd_r_add_comment(x, "forward", z, n);          /* -m comment ... */
657                                 fwd_xt_append_rule(x, "forwardings");           /* -A forwardings */
658                         }
659                 }
660         }
661
662         /* Build DNAT rules */
663         for( e = z->redirects; e && (r = &e->section.redirect); e = e->next )
664         {
665                 printf("\n# Net %s (%s) - redirect Z:%s N:%s I:%s\n",
666                         n->name, n->ifname, z->name, n->name, n->ifname);
667
668                 /* DNAT */
669                 if( (x = fwd_xt_init_rule(h_nat)) != NULL )
670                 {
671                         fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
672                         fwd_xt_parse_src(x, r->src_ip, 0);                      /* -s ... */
673                         fwd_xt_parse_dest(x, a, 0);                                     /* -d ... */
674                         fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
675                         fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
676                         fwd_r_add_dport(x, r->src_dport);                       /* --dport ... */
677                         fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
678                         fwd_r_add_dnattarget(x, r->dest_ip, r->dest_port);      /* -j DNAT ... */
679                         fwd_r_add_comment(x, "redir", z, n);            /* -m comment ... */
680                         fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
681                 }
682
683                 /* Forward */
684                 if( (x = fwd_xt_init_rule(h_filter)) != NULL )
685                 {
686                         fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
687                         fwd_xt_parse_src(x, r->src_ip, 0);                      /* -s ... */
688                         fwd_xt_parse_dest(x, r->dest_ip, 0);            /* -d ... */
689                         fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
690                         fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
691                         fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
692                         fwd_r_add_dport(x, r->dest_port);                       /* --dport ... */
693                         fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_accept */
694                         fwd_r_add_comment(x, "redir", z, n);            /* -m comment ... */
695                         fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
696                 }
697
698                 /* Add loopback rule if neither src_ip nor src_mac are defined */
699                 if( !r->src_ip && !r->src_mac )
700                 {
701                         if( (x = fwd_xt_init_rule(h_nat)) != NULL )
702                         {
703                                 fwd_xt_parse_in(x, n, 1);                               /* -i ! ... */
704                                 fwd_xt_parse_dest(x, r->dest_ip, 0);    /* -d ... */
705                                 fwd_xt_parse_proto(x, r->proto, 0);             /* -p ... */
706                                 fwd_r_add_sport(x, r->src_port);                /* --sport ... */
707                                 fwd_r_add_dport(x, r->src_dport);               /* --dport ... */
708                                 fwd_xt_get_target(x, "MASQUERADE");             /* -j MASQUERADE */
709                                 fwd_r_add_comment(x, "redir", z, n);    /* -m comment ... */
710                                 fwd_xt_append_rule(x, "loopback");              /* -A loopback */
711                         }
712                 }
713         }
714
715         /* Build rules */
716         for( e = z->rules; e && (c = &e->section.rule); e = e->next )
717         {
718                 /* Has destination, add forward rule for each network in target zone */
719                 if( c->dest )
720                 {
721                         for( n2 = c->dest->networks; n2; n2 = n2->next )
722                         {
723                                 printf("\n# Net %s (%s) - rule+dest"
724                                " Z:%s N:%s I:%s -> Z:%s N:%s I:%s\n",
725                                         n->name, n->ifname, z->name, n->name, n->ifname,
726                                         f->dest->name, n2->name, n2->ifname);
727
728                                 if( (x = fwd_xt_init_rule(h_filter)) != NULL )
729                                 {
730                                         fwd_xt_parse_in(x, n, 0);                               /* -i ... */
731                                         fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
732                                         fwd_xt_parse_src(x, c->src_ip, 0);              /* -s ... */
733                                         fwd_xt_parse_dest(x, c->dest_ip, 0);    /* -d ... */
734                                         fwd_xt_parse_proto(x, c->proto, 0);             /* -p ... */
735                                         fwd_r_add_icmptype(x, c->icmp_type);    /* --icmp-type ... */
736                                         fwd_r_add_srcmac(x, c->src_mac);                /* --mac-source ... */
737                                         fwd_r_add_sport(x, c->src_port);                /* --sport ... */
738                                         fwd_r_add_dport(x, c->dest_port);               /* --dport ... */
739                                         fwd_r_add_policytarget(x, c->target);   /* -j handle_... */
740                                         fwd_r_add_comment(x, "rule", z, n);             /* -m comment ... */
741                                         fwd_xt_append_rule(x, "rules");                 /* -A rules */
742                                 }
743                         }
744                 }
745
746                 /* No destination specified, treat it as input rule */
747                 else
748                 {
749                         printf("\n# Net %s (%s) - rule Z:%s N:%s I:%s\n",
750                                 n->name, n->ifname, z->name, n->name, n->ifname);
751
752                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
753                         {
754                                 fwd_xt_parse_in(x, n, 0);                               /* -i ... */
755                                 fwd_xt_parse_src(x, c->src_ip, 0);              /* -s ... */
756                                 fwd_xt_parse_dest(x, c->dest_ip, 0);    /* -d ... */
757                                 fwd_xt_parse_proto(x, c->proto, 0);             /* -p ... */
758                                 fwd_r_add_icmptype(x, c->icmp_type);    /* --icmp-type ... */
759                                 fwd_r_add_srcmac(x, c->src_mac);                /* --mac-source ... */
760                                 fwd_r_add_sport(x, c->src_port);                /* --sport ... */
761                                 fwd_r_add_dport(x, c->dest_port);               /* --dport ... */
762                                 fwd_r_add_policytarget(x, c->target);   /* -j handle_... */
763                                 fwd_r_add_comment(x, "rule", z, n);             /* -m comment ... */
764                                 fwd_xt_append_rule(x, "rules");                 /* -A rules */
765                         }
766                 }
767         }
768
769         if( !iptc_commit(h_nat) )
770                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
771
772         if( !iptc_commit(h_filter) )
773                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
774
775         iptc_free(h_nat);
776         iptc_free(h_filter);
777 }
778
779
780 static void fwd_ipt_delif_table(struct iptc_handle *h, const char *net)
781 {
782         const struct xt_entry_match *m;
783         const struct ipt_entry *e;
784         const char *chain, *comment;
785         size_t off = 0, num = 0;
786
787         /* iterate chains */
788         for( chain = iptc_first_chain(h); chain;
789              chain = iptc_next_chain(h)
790         ) {
791                 /* iterate rules */
792                 for( e = iptc_first_rule(chain, h), num = 0; e;
793                      e = iptc_next_rule(e, h), num++
794                 ) {
795                         repeat_rule:
796
797                         /* skip entries w/o matches */
798                         if( ! e->target_offset )
799                                 continue;
800
801                         /* iterate matches */
802                         for( off = sizeof(struct ipt_entry);
803                              off < e->target_offset;
804                              off += m->u.match_size
805                         ) {
806                                 m = (void *)e + off;
807
808                                 /* yay */
809                                 if( ! strcmp(m->u.user.name, "comment") )
810                                 {
811                                         /* better use struct_xt_comment_info but well... */
812                                         comment = (void *)m + sizeof(struct xt_entry_match);
813
814                                         if( fwd_r_cmp("net=", comment, net) )
815                                         {
816                                                 e = iptc_next_rule(e, h);
817                                                 iptc_delete_num_entry(chain, num, h);
818
819                                                 if( e != NULL )
820                                                         goto repeat_rule;
821                                                 else
822                                                         break;
823                                         }
824                                 }
825                         }
826                 }
827         }
828 }
829
830 void fwd_ipt_delif(struct fwd_handle *h, const char *net)
831 {
832         struct iptc_handle *h_filter, *h_nat;
833
834         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
835                 fwd_fatal("Unable to obtain libiptc handle");
836
837
838         printf("\n\n#\n# delif(%s)\n#\n", net);
839
840         /* delete network related rules */
841         fwd_ipt_delif_table(h_nat, net);
842         fwd_ipt_delif_table(h_filter, net);
843
844
845         if( !iptc_commit(h_nat) )
846                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
847
848         if( !iptc_commit(h_filter) )
849                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
850
851         iptc_free(h_nat);
852         iptc_free(h_filter);
853 }
854
855 void fwd_ipt_chgif(struct fwd_handle *h, const char *net)
856 {
857         /* XXX: should alter rules in-place, tbd */
858         fwd_ipt_delif(h, net);
859         fwd_ipt_addif(h, net);
860 }
861
862
863 static void fwd_ipt_clear_ruleset_table(struct iptc_handle *h)
864 {
865         const char *chain;
866
867         /* pass 1: flush all chains */
868         for( chain = iptc_first_chain(h); chain;
869              chain = iptc_next_chain(h)
870         ) {
871                 iptc_flush_entries(chain, h);
872         }
873
874         /* pass 2: remove user defined chains */
875         for( chain = iptc_first_chain(h); chain;
876              chain = iptc_next_chain(h)
877         ) {
878                 if( ! iptc_builtin(chain, h) )
879                         iptc_delete_chain(chain, h);
880         }
881 }
882
883 void fwd_ipt_clear_ruleset(struct fwd_handle *h)
884 {
885         struct iptc_handle *h_filter, *h_nat;
886
887         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
888                 fwd_fatal("Unable to obtain libiptc handle");
889
890         /* flush tables */
891         fwd_ipt_clear_ruleset_table(h_nat);
892         fwd_ipt_clear_ruleset_table(h_filter);
893
894         /* revert policies */
895         fwd_r_set_policy(h_filter, "INPUT", "ACCEPT");
896         fwd_r_set_policy(h_filter, "OUTPUT", "ACCEPT");
897         fwd_r_set_policy(h_filter, "FORWARD", "ACCEPT");        
898
899
900         if( !iptc_commit(h_nat) )
901                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
902
903         if( !iptc_commit(h_filter) )
904                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
905
906         iptc_free(h_nat);
907         iptc_free(h_filter);
908 }
909