contrib: fwd - initial C implementation of the uci firewall
[project/luci.git] / contrib / fwd / src / fwd_config.c
1 /*
2  * fwd - OpenWrt firewall daemon - config parsing
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_config.h"
23
24 #include "ucix.h"
25
26
27 #define fwd_read_error(...) do {  \
28         fprintf(stderr, "ERROR: ");   \
29         fprintf(stderr, __VA_ARGS__); \
30         fprintf(stderr, "\n");        \
31         return;                       \
32 } while(0)
33
34
35 /*
36  * Parse helpers
37  */
38 static int
39 fwd_read_policy(struct uci_context *uci, const char *s, const char *o)
40 {
41         const char *val = ucix_get_option(uci, "firewall", s, o);
42
43         if( val != NULL )
44         {
45                 switch( val[0] )
46                 {
47                         case 'D':
48                         case 'd':
49                                 return FWD_P_DROP;
50
51                         case 'R':
52                         case 'r':
53                                 return FWD_P_REJECT;
54
55                         case 'A':
56                         case 'a':
57                                 return FWD_P_ACCEPT;
58                 }
59         }
60
61         return FWD_P_UNSPEC;
62 }
63
64 static int
65 fwd_read_bool(struct uci_context *uci, const char *s, const char *o, int d)
66 {
67         const char *val = ucix_get_option(uci, "firewall", s, o);
68
69         if( val != NULL )
70         {
71                 if( !strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1") )
72                         return 1;
73                 else
74                         return 0;
75         }
76
77         return d;
78 }
79
80 static unsigned int
81 fwd_read_uint(struct uci_context *uci, const char *s, const char *o, unsigned int d)
82 {
83         const char *val = ucix_get_option(uci, "firewall", s, o);
84
85         if( val != NULL )
86         {
87                 return atoi(val);
88         }
89
90         return d;
91 }
92
93 static int
94 fwd_read_cidr(struct uci_context *uci, const char *s, const char *o, struct fwd_cidr **c)
95 {
96         const char *val = ucix_get_option(uci, "firewall", s, o);
97         char ip[32], prefix[32];
98         struct in_addr ina;
99
100         memset(ip, 0, 32);
101         memset(prefix, 0, 32);
102
103         if( val == NULL )
104         {
105                 return 0;
106         }
107         else if( (strlen(val) < 32) && (sscanf(val, "%[^/]/%s", ip, prefix) > 0) )
108         {
109                 if( !(*c = fwd_alloc_ptr(struct fwd_cidr)) )
110                         goto inval;
111
112                 if( inet_aton(ip, &ina) )
113                 {
114                         (*c)->addr.s_addr = ina.s_addr;
115
116                         if( strchr(prefix, '.') )
117                         {
118                                 if( inet_aton(prefix, &ina) )
119                                 {
120                                         (*c)->prefix = 32;
121                                         ina.s_addr = ntohl(ina.s_addr);
122
123                                         while( !(ina.s_addr & 1) )
124                                         {
125                                                 ina.s_addr >>= 1;
126                                                 (*c)->prefix--;
127                                         }
128                                 }
129                                 else
130                                 {
131                                         goto inval;
132                                 }
133                         }
134                         else
135                         {
136                                 (*c)->prefix = prefix[0] ? atoi(prefix) : 32;
137
138                                 if( ((*c)->prefix < 0) || ((*c)->prefix > 32) )
139                                 {
140                                         goto inval;
141                                 }
142                         }
143
144                         return 0;
145                 }
146         }
147
148         inval:
149         fwd_free_ptr(*c);
150         return -1;
151 }
152
153 static int
154 fwd_read_mac(struct uci_context *uci, const char *s, const char *o, struct fwd_mac **m)
155 {
156         const char *val = ucix_get_option(uci, "firewall", s, o);
157
158         if( val == NULL )
159         {
160                 return 0;
161         }
162         else
163         {
164                 if( (*m = fwd_alloc_ptr(struct fwd_mac)) != NULL )
165                 {
166                         if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
167                                 (unsigned int *)&(*m)->mac[0], (unsigned int *)&(*m)->mac[1],
168                                 (unsigned int *)&(*m)->mac[2], (unsigned int *)&(*m)->mac[3],
169                                 (unsigned int *)&(*m)->mac[4], (unsigned int *)&(*m)->mac[5]) == 6
170                         ) {
171                                 return 0;
172                         }
173                 }
174         }
175
176         fwd_free_ptr(*m);
177         return -1;
178 }
179
180 static int
181 fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
182 {
183         const char *val = ucix_get_option(uci, "firewall", s, o);
184         int min = -1;
185         int max = -1;
186         unsigned int tmp;
187
188         if( val == NULL )
189         {
190                 return 0;
191         }
192         else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
193         {
194                 if( max == -1 )
195                 {
196                         max = min;
197                 }
198                 else if( min > max )
199                 {
200                         tmp = max;
201                         max = min;
202                         min = tmp;
203                 }
204
205                 if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
206                 {
207                         if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
208                         {
209                                 (*p)->min = min;
210                                 (*p)->max = max;
211                                 return 0;
212                         }
213                 }
214         }
215
216         fwd_free_ptr(*p);
217         return -1;
218 }
219
220 static int
221 fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
222 {
223         const char *val = ucix_get_option(uci, "firewall", s, o);
224         int proto;
225
226         if( val == NULL )
227         {
228                 return 0;
229         }
230         else
231         {
232                 if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
233                 {
234                         proto = atoi(val);
235
236                         if( !strcasecmp(val, "all") )
237                         {
238                                 (*p)->type  = FWD_PR_ALL;
239                                 (*p)->proto = 0;
240                         }
241                         else if( !strcasecmp(val, "icmp") )
242                         {
243                                 (*p)->type  = FWD_PR_ICMP;
244                                 (*p)->proto = 0;
245                         }
246                         else if( !strcasecmp(val, "udp") )
247                         {
248                                 (*p)->type  = FWD_PR_UDP;
249                                 (*p)->proto = 0;
250                         }
251                         else if( !strcasecmp(val, "tcp") )
252                         {
253                                 (*p)->type  = FWD_PR_TCP;
254                                 (*p)->proto = 0;
255                         }
256                         else if( !strcasecmp(val, "tcpudp") )
257                         {
258                                 (*p)->type  = FWD_PR_TCPUDP;
259                                 (*p)->proto = 0;
260                         }
261                         else if( proto > 0 )
262                         {
263                                 (*p)->type  = FWD_PR_CUSTOM;
264                                 (*p)->proto = proto;
265                         }
266                         else
267                         {
268                                 goto inval;
269                         }
270
271                         return 0;
272                 }
273         }
274
275         inval:
276         fwd_free_ptr(*p);
277         return -1;
278 }
279
280 static int
281 fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
282 {
283         const char *val = ucix_get_option(uci, "firewall", s, o);
284         unsigned int type, code;
285
286         if( val == NULL )
287         {
288                 return 0;
289         }
290         else
291         {
292                 if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
293                 {
294                         if( sscanf(val, "%u/%u", &type, &code) == 2 )
295                         {
296                                 if( (type > 255) || (code > 255) )
297                                         goto inval;
298
299                                 (*i)->type = type;
300                                 (*i)->code = code;
301
302                                 return 0;
303                         }
304
305                         else if( sscanf(val, "%u", &type) == 1 )
306                         {
307                                 if( type > 255 )
308                                         goto inval;
309
310                                 (*i)->type = type;
311                                 (*i)->code = -1;
312
313                                 return 0;
314                         }
315
316                         /* XXX: no validity check here but I do not want to
317                                 duplicate libipt_icmp.c ... */
318                         else if( sscanf(val, "%31s", (*i)->name) == 1 )
319                         {
320                                 return 0;
321                         }
322                 }
323         }
324
325         inval:
326         fwd_free_ptr(*i);
327         return -1;
328 }
329
330 static const char *
331 fwd_read_string(struct uci_context *uci, const char *s, const char *o)
332 {
333         return ucix_get_option(uci, "firewall", s, o);
334 }
335
336
337 static void
338 fwd_append_config(struct fwd_data *h, struct fwd_data *a)
339 {
340         while( h->next )
341                 h = h->next;
342
343         h->next = a;
344 }
345
346
347 /*
348  * config defaults
349  */
350 static void fwd_read_defaults_cb(
351         struct uci_context *uci,
352         const char *s, struct fwd_defaults *d
353 ) {
354         d->input        = fwd_read_policy(uci, s, "input");
355         d->forward      = fwd_read_policy(uci, s, "forward");
356         d->output       = fwd_read_policy(uci, s, "output");
357         d->syn_flood    = fwd_read_bool(uci, s, "syn_flood", 1);
358         d->syn_rate     = fwd_read_uint(uci, s, "syn_rate", 25);
359         d->syn_burst    = fwd_read_uint(uci, s, "syn_burst", 50);
360         d->drop_invalid = fwd_read_bool(uci, s, "drop_invalid", 1);
361 }
362
363 static struct fwd_data *
364 fwd_read_defaults(struct uci_context *uci)
365 {
366         struct fwd_data *dt;
367         struct fwd_defaults d;
368
369         if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
370         {
371                 memset(&d, 0, sizeof(d));
372
373                 ucix_for_each_section_type(uci, "firewall", "defaults",
374                         (void *)fwd_read_defaults_cb, &d);
375
376                 memcpy(&dt->section.defaults, &d, sizeof(d));
377
378                 dt->type = FWD_S_DEFAULTS;
379                 dt->next = NULL;
380
381                 return dt;
382         }
383
384         return NULL;
385 }
386
387
388 /*
389  * config zone
390  */
391 static void fwd_read_zone_networks_cb(
392         const char *net, struct fwd_network_list **np
393 ) {
394         struct fwd_network_list *nn;
395
396         if( (nn = fwd_alloc_ptr(struct fwd_network_list)) != NULL )
397         {
398                 nn->name = strdup(net);
399                 nn->next = *np;
400                 *np = nn;
401         }
402 }
403
404 static void fwd_read_zones_cb(
405         struct uci_context *uci,
406         const char *s, struct fwd_data_conveyor *cv
407 ) {
408         struct fwd_data *dtn;
409         struct fwd_network_list *net = NULL;
410         const char *name;
411
412         if( !(name = fwd_read_string(uci, s, "name")) )
413                 fwd_read_error("section '%s' is missing 'name' option!", s);
414
415         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
416         {
417                 dtn->section.zone.name      = strdup(name);
418                 dtn->section.zone.masq      = fwd_read_bool(uci, s, "masq", 0);
419                 dtn->section.zone.mtu_fix   = fwd_read_bool(uci, s, "mtu_fix", 0);
420                 dtn->section.zone.conntrack = fwd_read_bool(uci, s, "conntrack", 0);
421
422                 dtn->section.zone.input     = fwd_read_policy(uci, s, "input")
423                         ?: cv->head->section.defaults.input   ?: FWD_P_DROP;
424
425                 dtn->section.zone.forward   = fwd_read_policy(uci, s, "forward")
426                         ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
427
428                 dtn->section.zone.output    = fwd_read_policy(uci, s, "output")
429                         ?: cv->head->section.defaults.output  ?: FWD_P_DROP;
430
431                 /* try to parse option/list network ... */
432                 if( ucix_for_each_list(uci, "firewall", s, "network",
433                         (void *)&fwd_read_zone_networks_cb, &net) < 0 )
434                 {
435                         /* ... didn't work, fallback to option name */
436                         fwd_read_zone_networks_cb(name, &net);
437                 }
438
439                 dtn->section.zone.networks = net;
440                 dtn->type = FWD_S_ZONE;
441                 dtn->next = cv->cursor;
442                 cv->cursor = dtn;
443         }
444 }
445
446 static struct fwd_data *
447 fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
448 {
449         struct fwd_data_conveyor cv;
450
451         cv.cursor = NULL;
452         cv.head = def;
453
454         ucix_for_each_section_type(uci, "firewall", "zone",
455                 (void *)fwd_read_zones_cb, &cv);
456
457         return cv.cursor;
458 }
459
460
461 /*
462  * config forwarding
463  */
464 static void fwd_read_forwards_cb(
465         struct uci_context *uci,
466         const char *s, struct fwd_data_conveyor *cv
467 ) {
468         const char *src, *dest;
469         struct fwd_data *dtn;
470         struct fwd_zone *zsrc  = NULL;
471         struct fwd_zone *zdest = NULL;
472
473         if( (src = fwd_read_string(uci, s, "src")) != NULL )
474         {
475                 if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
476                         fwd_read_error("section '%s' references unknown src zone '%s'!", s, src);
477         }
478
479         if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
480         {
481                 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
482                         fwd_read_error("section '%s' references unknown dest zone '%s'!", s, dest);
483         }
484         
485         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
486         {
487                 dtn->section.forwarding.src = zsrc;
488                 dtn->section.forwarding.dest = zdest;
489                 dtn->section.forwarding.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
490                 dtn->section.forwarding.masq = fwd_read_bool(uci, s, "masq", 0);
491
492                 dtn->type = FWD_S_FORWARD;
493                 dtn->next = cv->cursor;
494                 cv->cursor = dtn;
495         }
496         else
497         {
498                 fwd_read_error("out of memory while parsing config!");
499         }
500 }
501
502 static struct fwd_data *
503 fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
504 {
505         struct fwd_data_conveyor cv;
506
507         cv.cursor = NULL;
508         cv.head = zones;
509
510         ucix_for_each_section_type(uci, "firewall", "forwarding",
511                 (void *)fwd_read_forwards_cb, &cv);
512
513         return cv.cursor;
514 }
515
516
517 /*
518  * config redirect
519  */
520 static void fwd_read_redirects_cb(
521         struct uci_context *uci,
522         const char *s, struct fwd_data_conveyor *cv
523 ) {
524         const char *src;
525         struct fwd_data *dtn  = NULL;
526         struct fwd_zone *zsrc = NULL;
527
528         /* check zone */
529         if( !(src = fwd_read_string(uci, s, "src")) )
530                 fwd_read_error(
531                         "section '%s' is missing 'src' option!",
532                         s
533                 );
534
535         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
536                 fwd_read_error(
537                         "section '%s' references unknown src zone '%s'!",
538                         s, src
539                 );
540
541         /* uci context, section, name, type */
542         fwd_check_option(uci, s, src_ip, cidr);
543         fwd_check_option(uci, s, src_mac, mac);
544         fwd_check_option(uci, s, src_port, portrange);
545         fwd_check_option(uci, s, src_dport, portrange);
546         fwd_check_option(uci, s, dest_ip, cidr);
547         fwd_check_option(uci, s, dest_port, portrange);
548         fwd_check_option(uci, s, proto, proto);
549         
550         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
551         {
552                 dtn->section.redirect.proto     = proto;
553                 dtn->section.redirect.src       = zsrc;
554                 dtn->section.redirect.src_ip    = src_ip;
555                 dtn->section.redirect.src_mac   = src_mac;
556                 dtn->section.redirect.src_port  = src_port;
557                 dtn->section.redirect.src_dport = src_dport;
558                 dtn->section.redirect.dest_ip   = dest_ip;
559                 dtn->section.redirect.dest_port = dest_port;
560
561                 dtn->type = FWD_S_REDIRECT;
562                 dtn->next = cv->cursor;
563                 cv->cursor = dtn;
564         }
565         else
566         {
567                 fwd_read_error("out of memory while parsing config!");
568         }
569 }
570
571 static struct fwd_data *
572 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
573 {
574         struct fwd_data_conveyor cv;
575
576         cv.cursor = NULL;
577         cv.head = zones;
578
579         ucix_for_each_section_type(uci, "firewall", "redirect",
580                 (void *)fwd_read_redirects_cb, &cv);
581
582         return cv.cursor;
583 }
584
585
586 /*
587  * config rule
588  */
589 static void fwd_read_rules_cb(
590         struct uci_context *uci,
591         const char *s, struct fwd_data_conveyor *cv
592 ) {
593         const char *src, *dest;
594         struct fwd_data *dtn   = NULL;
595         struct fwd_zone *zsrc  = NULL;
596         struct fwd_zone *zdest = NULL;
597
598         /* check zones */
599         if( !(src = fwd_read_string(uci, s, "src")) )
600                 fwd_read_error(
601                         "section '%s' is missing 'src' option!",
602                         s
603                 );
604
605         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
606                 fwd_read_error(
607                         "section '%s' references unknown src zone '%s'!",
608                         s, src
609                 );
610
611         if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
612                 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
613                         fwd_read_error(
614                                 "section '%s' references unknown dest zone '%s'!",
615                                 s, dest
616                         );
617
618         /* uci context, section, name, type */
619         fwd_check_option(uci, s, src_ip, cidr);
620         fwd_check_option(uci, s, src_mac, mac);
621         fwd_check_option(uci, s, src_port, portrange);
622         fwd_check_option(uci, s, dest_ip, cidr);
623         fwd_check_option(uci, s, dest_port, portrange);
624         fwd_check_option(uci, s, proto, proto);
625         fwd_check_option(uci, s, icmptype, icmptype);
626         
627         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
628         {
629                 dtn->section.rule.proto     = proto;
630                 dtn->section.rule.icmp_type = icmptype;
631                 dtn->section.rule.src       = zsrc;
632                 dtn->section.rule.src_ip    = src_ip;
633                 dtn->section.rule.src_mac   = src_mac;
634                 dtn->section.rule.src_port  = src_port;
635                 dtn->section.rule.dest      = zdest;
636                 dtn->section.rule.dest_ip   = dest_ip;
637                 dtn->section.rule.dest_port = dest_port;
638                 dtn->section.rule.target    = fwd_read_policy(uci, s, "target");
639
640                 dtn->type = FWD_S_RULE;
641                 dtn->next = cv->cursor;
642                 cv->cursor = dtn;
643         }
644         else
645         {
646                 fwd_read_error("out of memory while parsing config!");
647         }
648 }
649
650 static struct fwd_data *
651 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
652 {
653         struct fwd_data_conveyor cv;
654
655         cv.cursor = NULL;
656         cv.head = zones;
657
658         ucix_for_each_section_type(uci, "firewall", "rule",
659                 (void *)fwd_read_rules_cb, &cv);
660
661         return cv.cursor;
662 }
663
664
665 /*
666  * config include
667  */
668 static void fwd_read_includes_cb(
669         struct uci_context *uci,
670         const char *s, struct fwd_data_conveyor *cv
671 ) {
672         const char *path = fwd_read_string(uci, s, "path");
673         struct fwd_data *dtn = NULL;
674
675         if( path != NULL )
676         {
677                 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
678                 {
679                         dtn->section.include.path = strdup(path);
680
681                         dtn->type = FWD_S_INCLUDE;
682                         dtn->next = cv->cursor;
683                         cv->cursor = dtn;
684                 }
685                 else
686                 {
687                         fwd_read_error("out of memory while parsing config!");
688                 }
689         }
690 }
691
692 static struct fwd_data *
693 fwd_read_includes(struct uci_context *uci)
694 {
695         struct fwd_data_conveyor cv;
696
697         cv.cursor = NULL;
698         cv.head   = NULL;
699
700         ucix_for_each_section_type(uci, "firewall", "include",
701                 (void *)fwd_read_includes_cb, &cv);
702
703         return cv.cursor;
704 }
705
706
707 /*
708  * config interface
709  */
710 static void fwd_read_network_data(
711         struct uci_context *uci, struct fwd_network_list *net
712 ) {
713         struct fwd_network_list *e;
714         const char *type, *ifname;
715
716         for( e = net; e; e = e->next )
717         {
718                 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
719                 {
720                         if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
721                                 fwd_read_error(
722                                         "section '%s' is missing 'ifname' option!",
723                                         e->name
724                                 );
725
726                         e->isalias = (strcmp(type, "alias") ? 0 : 1);
727                         e->ifname  = strdup(ifname);
728                 }
729         }
730 }
731
732 static void fwd_read_networks(
733         struct uci_context *uci, struct fwd_data *zones
734 ) {
735         struct fwd_data *e;
736
737         for( e = zones; e; e = e->next )
738                 if( e->type == FWD_S_ZONE )
739                         fwd_read_network_data(uci, e->section.zone.networks);
740 }
741
742 static void fwd_free_networks(struct fwd_network_list *h)
743 {
744         struct fwd_network_list *e = h;
745
746         while( h != NULL )
747         {
748                 e = h->next;
749
750                 fwd_free_ptr(h->name);
751                 fwd_free_ptr(h->ifname);
752                 fwd_free_ptr(h->addr);
753
754                 free(h);
755                 h = e;
756         }
757
758         e = h = NULL;
759 }
760
761
762
763 struct fwd_data * fwd_read_config(void)
764 {
765         struct uci_context *ctx;
766         struct fwd_data *defaults, *zones;
767
768         if( (ctx = ucix_init("firewall")) != NULL )
769         {
770                 if( !(defaults = fwd_read_defaults(ctx)) )
771                         goto error;
772
773                 if( !(zones = fwd_read_zones(ctx, defaults)) )
774                         goto error;
775
776                 fwd_append_config(defaults, zones);
777                 fwd_append_config(defaults, fwd_read_forwards(ctx, zones));
778                 fwd_append_config(defaults, fwd_read_redirects(ctx, zones));
779                 fwd_append_config(defaults, fwd_read_rules(ctx, zones));
780                 fwd_append_config(defaults, fwd_read_includes(ctx));
781
782                 ucix_cleanup(ctx);
783
784                 if( (ctx = ucix_init("network")) != NULL )
785                 {
786                         fwd_read_networks(ctx, zones);
787                         ucix_cleanup(ctx);
788
789                         return defaults;
790                 }
791         }
792
793         error:
794         if( ctx ) ucix_cleanup(ctx);
795         fwd_free_config(defaults);
796         fwd_free_config(zones);
797         return NULL;    
798 }
799
800
801 void fwd_free_config(struct fwd_data *h)
802 {
803         struct fwd_data *e = h;
804
805         while( h != NULL )
806         {
807                 e = h->next;
808
809                 switch(h->type)
810                 {
811                         case FWD_S_INCLUDE:
812                                 fwd_free_ptr(h->section.include.path);
813                                 break;
814
815                         case FWD_S_ZONE:
816                                 fwd_free_ptr(h->section.zone.name);
817                                 fwd_free_networks(h->section.zone.networks);
818                                 break;
819
820                         case FWD_S_REDIRECT:
821                                 fwd_free_ptr(h->section.redirect.src_ip);
822                                 fwd_free_ptr(h->section.redirect.src_mac);
823                                 fwd_free_ptr(h->section.redirect.src_port);
824                                 fwd_free_ptr(h->section.redirect.src_dport);
825                                 fwd_free_ptr(h->section.redirect.dest_ip);
826                                 fwd_free_ptr(h->section.redirect.dest_port);
827                                 fwd_free_ptr(h->section.redirect.proto);
828                                 break;
829
830                         case FWD_S_RULE:
831                                 fwd_free_ptr(h->section.rule.src_ip);
832                                 fwd_free_ptr(h->section.rule.src_mac);
833                                 fwd_free_ptr(h->section.rule.src_port);
834                                 fwd_free_ptr(h->section.rule.dest_ip);
835                                 fwd_free_ptr(h->section.rule.dest_port);
836                                 fwd_free_ptr(h->section.rule.proto);
837                                 fwd_free_ptr(h->section.rule.icmp_type);
838                                 break;
839
840                         case FWD_S_DEFAULTS:
841                         case FWD_S_FORWARD:
842                                 /* Make gcc happy */
843                                 break;
844                 }
845
846                 free(h);
847                 h = e;
848         }
849
850         e = h = NULL;
851 }
852
853
854 struct fwd_zone *
855 fwd_lookup_zone(struct fwd_data *h, const char *n)
856 {
857         struct fwd_data *e;
858
859         if( n != NULL )
860         {
861                 for( e = h; e; e = e->next )
862                 {
863                         if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
864                                 return &e->section.zone;
865                 }
866         }
867
868         return NULL;
869 }
870