Move mjpg-streamer.pot to the right directory
[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                                 fwd_log_info("Loading defaults");
500                                 fwd_ipt_defaults_create(e);
501                                 break;
502
503                         case FWD_S_INCLUDE:
504                                 fwd_log_info("Loading include: %s",
505                                         e->section.include.path);
506                                 break;
507
508                         case FWD_S_ZONE:
509                         case FWD_S_FORWARD:
510                         case FWD_S_REDIRECT:
511                         case FWD_S_RULE:
512                                 /* Make gcc happy */
513                                 break;
514                 }
515         }
516 }
517
518
519 static struct fwd_zone *
520 fwd_lookup_zone(struct fwd_handle *h, const char *net)
521 {
522         struct fwd_data *e;
523         struct fwd_network *n;
524
525         for( e = h->conf; e; e = e->next )
526                 if( e->type == FWD_S_ZONE )
527                         for( n = e->section.zone.networks; n; n = n->next )
528                                 if( !strcmp(n->name, net) )
529                                         return &e->section.zone;
530
531         return NULL;
532 }
533
534 static struct fwd_network *
535 fwd_lookup_network(struct fwd_zone *z, const char *net)
536 {
537         struct fwd_network *n;
538
539         for( n = z->networks; n; n = n->next )
540                 if( !strcmp(n->name, net) )
541                         return n;
542
543         return NULL;
544 }
545
546 void fwd_ipt_addif(struct fwd_handle *h, const char *net)
547 {
548         struct fwd_data *e;
549         struct fwd_zone *z;
550         struct fwd_rule *c;
551         struct fwd_redirect *r;
552         struct fwd_forwarding *f;
553         struct fwd_cidr *a, *a2;
554         struct fwd_network *n, *n2;
555         struct fwd_proto p;
556
557         struct fwd_xt_rule *x;
558         struct xtables_match *m;
559         struct xtables_target *t;
560
561         struct iptc_handle *h_filter, *h_nat;
562
563         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
564                 fwd_fatal("Unable to obtain libiptc handle");
565
566
567         if( !(z = fwd_lookup_zone(h, net)) )
568                 return;
569
570         if( !(n = fwd_lookup_network(z, net)) )
571                 return;
572
573         if( !(a = n->addr) || fwd_empty_cidr(a) )
574                 return;
575
576
577         fwd_log_info("Adding network %s (interface %s)",
578                 n->name, n->ifname);
579
580         /* Build masquerading rule */
581         if( z->masq )
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                 if( (x = fwd_xt_init_rule(h_filter)) != NULL )
596                 {
597                         p.type = FWD_PR_TCP;
598                         fwd_xt_parse_out(x, n, 0);                                      /* -o ... */
599                         fwd_xt_parse_proto(x, &p, 0);                           /* -p tcp */
600
601                         /* -m tcp --tcp-flags SYN,RST SYN */
602                         if( (m = fwd_xt_get_match(x, "tcp")) != NULL )
603                                 fwd_xt_parse_match(x, m, "--tcp-flags", "SYN,RST", "SYN");
604
605                         /* -j TCPMSS --clamp-mss-to-pmtu */
606                         if( (t = fwd_xt_get_target(x, "TCPMSS")) != NULL )
607                                 fwd_xt_parse_target(x, t, "--clamp-mss-to-pmtu");
608
609                         /* -m comment ... */
610                         fwd_r_add_comment(x, "mssfix", z, n);
611
612                         /* -A mssfix */
613                         fwd_xt_append_rule(x, "mssfix");
614                 }
615         }
616
617         /* Build intra-zone forwarding rules */
618         for( n2 = z->networks; n2; n2 = n2->next )
619         {
620                 if( (a2 = n2->addr) != NULL )
621                 {
622                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
623                         {
624                                 fwd_xt_parse_in(x, n, 0);                               /* -i ... */
625                                 fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
626                                 fwd_r_add_policytarget(x, z->forward);  /* -j handle_... */
627                                 fwd_r_add_comment(x, "zone", z, n);             /* -m comment ... */
628                                 fwd_xt_append_rule(x, "zones");                 /* -A zones */
629                         }
630                 }
631         }
632
633         /* Build inter-zone forwarding rules */
634         for( e = z->forwardings; e && (f = &e->section.forwarding); e = e->next )
635         {
636                 for( n2 = f->dest->networks; n2; n2 = n2->next )
637                 {
638                         /* Build forwarding rule */
639                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
640                         {
641                                 fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
642                                 fwd_xt_parse_out(x, n2, 0);                                     /* -o ... */
643                                 fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_... */
644                                 fwd_r_add_comment(x, "forward", z, n);          /* -m comment ... */
645                                 fwd_xt_append_rule(x, "forwardings");           /* -A forwardings */
646                         }
647                 }
648         }
649
650         /* Build DNAT rules */
651         for( e = z->redirects; e && (r = &e->section.redirect); e = e->next )
652         {
653                 /* DNAT */
654                 if( (x = fwd_xt_init_rule(h_nat)) != NULL )
655                 {
656                         fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
657                         fwd_xt_parse_src(x, r->src_ip, 0);                      /* -s ... */
658                         fwd_xt_parse_dest(x, a, 0);                                     /* -d ... */
659                         fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
660                         fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
661                         fwd_r_add_dport(x, r->src_dport);                       /* --dport ... */
662                         fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
663                         fwd_r_add_dnattarget(x, r->dest_ip, r->dest_port);      /* -j DNAT ... */
664                         fwd_r_add_comment(x, "redir", z, n);            /* -m comment ... */
665                         fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
666                 }
667
668                 /* Forward */
669                 if( (x = fwd_xt_init_rule(h_filter)) != 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, r->dest_ip, 0);            /* -d ... */
674                         fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
675                         fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
676                         fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
677                         fwd_r_add_dport(x, r->dest_port);                       /* --dport ... */
678                         fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_accept */
679                         fwd_r_add_comment(x, "redir", z, n);            /* -m comment ... */
680                         fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
681                 }
682
683                 /* Add loopback rule if neither src_ip nor src_mac are defined */
684                 if( !r->src_ip && !r->src_mac )
685                 {
686                         if( (x = fwd_xt_init_rule(h_nat)) != NULL )
687                         {
688                                 fwd_xt_parse_in(x, n, 1);                               /* -i ! ... */
689                                 fwd_xt_parse_dest(x, r->dest_ip, 0);    /* -d ... */
690                                 fwd_xt_parse_proto(x, r->proto, 0);             /* -p ... */
691                                 fwd_r_add_sport(x, r->src_port);                /* --sport ... */
692                                 fwd_r_add_dport(x, r->src_dport);               /* --dport ... */
693                                 fwd_xt_get_target(x, "MASQUERADE");             /* -j MASQUERADE */
694                                 fwd_r_add_comment(x, "redir", z, n);    /* -m comment ... */
695                                 fwd_xt_append_rule(x, "loopback");              /* -A loopback */
696                         }
697                 }
698         }
699
700         /* Build rules */
701         for( e = z->rules; e && (c = &e->section.rule); e = e->next )
702         {
703                 /* Has destination, add forward rule for each network in target zone */
704                 if( c->dest )
705                 {
706                         for( n2 = c->dest->networks; n2; n2 = n2->next )
707                         {
708                                 if( (x = fwd_xt_init_rule(h_filter)) != NULL )
709                                 {
710                                         fwd_xt_parse_in(x, n, 0);                               /* -i ... */
711                                         fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
712                                         fwd_xt_parse_src(x, c->src_ip, 0);              /* -s ... */
713                                         fwd_xt_parse_dest(x, c->dest_ip, 0);    /* -d ... */
714                                         fwd_xt_parse_proto(x, c->proto, 0);             /* -p ... */
715                                         fwd_r_add_icmptype(x, c->icmp_type);    /* --icmp-type ... */
716                                         fwd_r_add_srcmac(x, c->src_mac);                /* --mac-source ... */
717                                         fwd_r_add_sport(x, c->src_port);                /* --sport ... */
718                                         fwd_r_add_dport(x, c->dest_port);               /* --dport ... */
719                                         fwd_r_add_policytarget(x, c->target);   /* -j handle_... */
720                                         fwd_r_add_comment(x, "rule", z, n);             /* -m comment ... */
721                                         fwd_xt_append_rule(x, "rules");                 /* -A rules */
722                                 }
723                         }
724                 }
725
726                 /* No destination specified, treat it as input rule */
727                 else
728                 {
729                         if( (x = fwd_xt_init_rule(h_filter)) != NULL )
730                         {
731                                 fwd_xt_parse_in(x, n, 0);                               /* -i ... */
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         if( !iptc_commit(h_nat) )
747                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
748
749         if( !iptc_commit(h_filter) )
750                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
751
752         iptc_free(h_nat);
753         iptc_free(h_filter);
754 }
755
756
757 static void fwd_ipt_delif_table(struct iptc_handle *h, const char *net)
758 {
759         const struct xt_entry_match *m;
760         const struct ipt_entry *e;
761         const char *chain, *comment;
762         size_t off = 0, num = 0;
763
764         /* iterate chains */
765         for( chain = iptc_first_chain(h); chain;
766              chain = iptc_next_chain(h)
767         ) {
768                 /* iterate rules */
769                 for( e = iptc_first_rule(chain, h), num = 0; e;
770                      e = iptc_next_rule(e, h), num++
771                 ) {
772                         repeat_rule:
773
774                         /* skip entries w/o matches */
775                         if( ! e->target_offset )
776                                 continue;
777
778                         /* iterate matches */
779                         for( off = sizeof(struct ipt_entry);
780                              off < e->target_offset;
781                              off += m->u.match_size
782                         ) {
783                                 m = (void *)e + off;
784
785                                 /* yay */
786                                 if( ! strcmp(m->u.user.name, "comment") )
787                                 {
788                                         /* better use struct_xt_comment_info but well... */
789                                         comment = (void *)m + sizeof(struct xt_entry_match);
790
791                                         if( fwd_r_cmp("net=", comment, net) )
792                                         {
793                                                 e = iptc_next_rule(e, h);
794                                                 iptc_delete_num_entry(chain, num, h);
795
796                                                 if( e != NULL )
797                                                         goto repeat_rule;
798                                                 else
799                                                         break;
800                                         }
801                                 }
802                         }
803                 }
804         }
805 }
806
807 void fwd_ipt_delif(struct fwd_handle *h, const char *net)
808 {
809         struct iptc_handle *h_filter, *h_nat;
810
811         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
812                 fwd_fatal("Unable to obtain libiptc handle");
813
814
815         fwd_log_info("Removing network %s", net);
816
817         /* delete network related rules */
818         fwd_ipt_delif_table(h_nat, net);
819         fwd_ipt_delif_table(h_filter, net);
820
821
822         if( !iptc_commit(h_nat) )
823                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
824
825         if( !iptc_commit(h_filter) )
826                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
827
828         iptc_free(h_nat);
829         iptc_free(h_filter);
830 }
831
832 void fwd_ipt_chgif(struct fwd_handle *h, const char *net)
833 {
834         /* XXX: should alter rules in-place, tbd */
835         fwd_ipt_delif(h, net);
836         fwd_ipt_addif(h, net);
837 }
838
839
840 static void fwd_ipt_clear_ruleset_table(struct iptc_handle *h)
841 {
842         const char *chain;
843
844         /* pass 1: flush all chains */
845         for( chain = iptc_first_chain(h); chain;
846              chain = iptc_next_chain(h)
847         ) {
848                 iptc_flush_entries(chain, h);
849         }
850
851         /* pass 2: remove user defined chains */
852         for( chain = iptc_first_chain(h); chain;
853              chain = iptc_next_chain(h)
854         ) {
855                 if( ! iptc_builtin(chain, h) )
856                         iptc_delete_chain(chain, h);
857         }
858 }
859
860 void fwd_ipt_clear_ruleset(struct fwd_handle *h)
861 {
862         struct iptc_handle *h_filter, *h_nat;
863
864         if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
865                 fwd_fatal("Unable to obtain libiptc handle");
866
867         /* flush tables */
868         fwd_ipt_clear_ruleset_table(h_nat);
869         fwd_ipt_clear_ruleset_table(h_filter);
870
871         /* revert policies */
872         fwd_r_set_policy(h_filter, "INPUT", "ACCEPT");
873         fwd_r_set_policy(h_filter, "OUTPUT", "ACCEPT");
874         fwd_r_set_policy(h_filter, "FORWARD", "ACCEPT");        
875
876
877         if( !iptc_commit(h_nat) )
878                 fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
879
880         if( !iptc_commit(h_filter) )
881                 fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
882
883         iptc_free(h_nat);
884         iptc_free(h_filter);
885 }
886