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