contrib/fwd: properly handle rules with proto=tcpudp
[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_data *dtn2 = NULL;
527         struct fwd_zone *zsrc = NULL;
528
529         /* check zone */
530         if( !(src = fwd_read_string(uci, s, "src")) )
531                 fwd_read_error(
532                         "section '%s' is missing 'src' option!",
533                         s
534                 );
535
536         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
537                 fwd_read_error(
538                         "section '%s' references unknown src zone '%s'!",
539                         s, src
540                 );
541
542         /* uci context, section, name, type */
543         fwd_check_option(uci, s, src_ip, cidr);
544         fwd_check_option(uci, s, src_mac, mac);
545         fwd_check_option(uci, s, src_port, portrange);
546         fwd_check_option(uci, s, src_dport, portrange);
547         fwd_check_option(uci, s, dest_ip, cidr);
548         fwd_check_option(uci, s, dest_port, portrange);
549         fwd_check_option(uci, s, proto, proto);
550         
551         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
552         {
553                 dtn->section.redirect.proto     = proto;
554                 dtn->section.redirect.src       = zsrc;
555                 dtn->section.redirect.src_ip    = src_ip;
556                 dtn->section.redirect.src_mac   = src_mac;
557                 dtn->section.redirect.src_port  = src_port;
558                 dtn->section.redirect.src_dport = src_dport;
559                 dtn->section.redirect.dest_ip   = dest_ip;
560                 dtn->section.redirect.dest_port = dest_port;
561
562                 dtn->type = FWD_S_REDIRECT;
563                 dtn->next = cv->cursor;
564                 cv->cursor = dtn;
565
566                 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
567                 {
568                         if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
569                             !(dtn2->section.redirect.proto = fwd_alloc_ptr(struct fwd_proto))
570                         ) {
571                                 fwd_free_ptr(dtn2);
572                                 fwd_read_error("out of memory while parsing config!");
573                         }
574
575                         dtn->section.redirect.proto->type = FWD_PR_UDP;
576                         dtn2->section.redirect.proto->type = FWD_PR_TCP;
577
578                         dtn2->section.redirect.src       = zsrc;
579                         dtn2->section.redirect.src_ip    = src_ip;
580                         dtn2->section.redirect.src_mac   = src_mac;
581                         dtn2->section.redirect.src_port  = src_port;
582                         dtn2->section.redirect.src_dport = src_dport;
583                         dtn2->section.redirect.dest_ip   = dest_ip;
584                         dtn2->section.redirect.dest_port = dest_port;
585
586                         dtn2->type = FWD_S_REDIRECT;
587                         dtn2->next = cv->cursor;
588                         cv->cursor = dtn2;
589                 }
590         }
591         else
592         {
593                 fwd_read_error("out of memory while parsing config!");
594         }
595 }
596
597 static struct fwd_data *
598 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
599 {
600         struct fwd_data_conveyor cv;
601
602         cv.cursor = NULL;
603         cv.head = zones;
604
605         ucix_for_each_section_type(uci, "firewall", "redirect",
606                 (void *)fwd_read_redirects_cb, &cv);
607
608         return cv.cursor;
609 }
610
611
612 /*
613  * config rule
614  */
615 static void fwd_read_rules_cb(
616         struct uci_context *uci,
617         const char *s, struct fwd_data_conveyor *cv
618 ) {
619         const char *src, *dest;
620         struct fwd_data *dtn   = NULL;
621         struct fwd_data *dtn2  = NULL;
622         struct fwd_zone *zsrc  = NULL;
623         struct fwd_zone *zdest = NULL;
624
625         /* check zones */
626         if( !(src = fwd_read_string(uci, s, "src")) )
627                 fwd_read_error(
628                         "section '%s' is missing 'src' option!",
629                         s
630                 );
631
632         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
633                 fwd_read_error(
634                         "section '%s' references unknown src zone '%s'!",
635                         s, src
636                 );
637
638         if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
639                 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
640                         fwd_read_error(
641                                 "section '%s' references unknown dest zone '%s'!",
642                                 s, dest
643                         );
644
645         /* uci context, section, name, type */
646         fwd_check_option(uci, s, src_ip, cidr);
647         fwd_check_option(uci, s, src_mac, mac);
648         fwd_check_option(uci, s, src_port, portrange);
649         fwd_check_option(uci, s, dest_ip, cidr);
650         fwd_check_option(uci, s, dest_port, portrange);
651         fwd_check_option(uci, s, proto, proto);
652         fwd_check_option(uci, s, icmptype, icmptype);
653         
654         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
655         {
656                 dtn->section.rule.proto     = proto;
657                 dtn->section.rule.icmp_type = icmptype;
658                 dtn->section.rule.src       = zsrc;
659                 dtn->section.rule.src_ip    = src_ip;
660                 dtn->section.rule.src_mac   = src_mac;
661                 dtn->section.rule.src_port  = src_port;
662                 dtn->section.rule.dest      = zdest;
663                 dtn->section.rule.dest_ip   = dest_ip;
664                 dtn->section.rule.dest_port = dest_port;
665                 dtn->section.rule.target    = fwd_read_policy(uci, s, "target");
666
667                 dtn->type = FWD_S_RULE;
668                 dtn->next = cv->cursor;
669                 cv->cursor = dtn;
670
671                 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
672                 {
673                         if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
674                             !(dtn2->section.rule.proto = fwd_alloc_ptr(struct fwd_proto))
675                         ) {
676                                 fwd_free_ptr(dtn2);
677                                 fwd_read_error("out of memory while parsing config!");
678                         }
679
680                         dtn->section.rule.proto->type = FWD_PR_UDP;
681                         dtn2->section.rule.proto->type = FWD_PR_TCP;
682
683                         dtn2->section.rule.src       = zsrc;
684                         dtn2->section.rule.src_ip    = src_ip;
685                         dtn2->section.rule.src_mac   = src_mac;
686                         dtn2->section.rule.src_port  = src_port;
687                         dtn2->section.rule.dest      = zdest;
688                         dtn2->section.rule.dest_ip   = dest_ip;
689                         dtn2->section.rule.dest_port = dest_port;
690                         dtn2->section.rule.target    = dtn->section.rule.target;
691
692                         dtn2->type = FWD_S_RULE;
693                         dtn2->next = cv->cursor;
694                         cv->cursor = dtn2;
695                 }
696         }
697         else
698         {
699                 fwd_read_error("out of memory while parsing config!");
700         }
701 }
702
703 static struct fwd_data *
704 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
705 {
706         struct fwd_data_conveyor cv;
707
708         cv.cursor = NULL;
709         cv.head = zones;
710
711         ucix_for_each_section_type(uci, "firewall", "rule",
712                 (void *)fwd_read_rules_cb, &cv);
713
714         return cv.cursor;
715 }
716
717
718 /*
719  * config include
720  */
721 static void fwd_read_includes_cb(
722         struct uci_context *uci,
723         const char *s, struct fwd_data_conveyor *cv
724 ) {
725         const char *path = fwd_read_string(uci, s, "path");
726         struct fwd_data *dtn = NULL;
727
728         if( path != NULL )
729         {
730                 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
731                 {
732                         dtn->section.include.path = strdup(path);
733
734                         dtn->type = FWD_S_INCLUDE;
735                         dtn->next = cv->cursor;
736                         cv->cursor = dtn;
737                 }
738                 else
739                 {
740                         fwd_read_error("out of memory while parsing config!");
741                 }
742         }
743 }
744
745 static struct fwd_data *
746 fwd_read_includes(struct uci_context *uci)
747 {
748         struct fwd_data_conveyor cv;
749
750         cv.cursor = NULL;
751         cv.head   = NULL;
752
753         ucix_for_each_section_type(uci, "firewall", "include",
754                 (void *)fwd_read_includes_cb, &cv);
755
756         return cv.cursor;
757 }
758
759
760 /*
761  * config interface
762  */
763 static void fwd_read_network_data(
764         struct uci_context *uci, struct fwd_network_list *net
765 ) {
766         struct fwd_network_list *e;
767         const char *type, *ifname;
768
769         for( e = net; e; e = e->next )
770         {
771                 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
772                 {
773                         if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
774                                 fwd_read_error(
775                                         "section '%s' is missing 'ifname' option!",
776                                         e->name
777                                 );
778
779                         e->isalias = (strcmp(type, "alias") ? 0 : 1);
780                         e->ifname  = strdup(ifname);
781                 }
782         }
783 }
784
785 static void fwd_read_networks(
786         struct uci_context *uci, struct fwd_data *zones
787 ) {
788         struct fwd_data *e;
789
790         for( e = zones; e; e = e->next )
791                 if( e->type == FWD_S_ZONE )
792                         fwd_read_network_data(uci, e->section.zone.networks);
793 }
794
795 static void fwd_free_networks(struct fwd_network_list *h)
796 {
797         struct fwd_network_list *e = h;
798
799         while( h != NULL )
800         {
801                 e = h->next;
802
803                 fwd_free_ptr(h->name);
804                 fwd_free_ptr(h->ifname);
805                 fwd_free_ptr(h->addr);
806
807                 free(h);
808                 h = e;
809         }
810
811         e = h = NULL;
812 }
813
814
815
816 struct fwd_data * fwd_read_config(void)
817 {
818         struct uci_context *ctx;
819         struct fwd_data *defaults, *zones;
820
821         if( (ctx = ucix_init("firewall")) != NULL )
822         {
823                 if( !(defaults = fwd_read_defaults(ctx)) )
824                         goto error;
825
826                 if( !(zones = fwd_read_zones(ctx, defaults)) )
827                         goto error;
828
829                 fwd_append_config(defaults, zones);
830                 fwd_append_config(defaults, fwd_read_forwards(ctx, zones));
831                 fwd_append_config(defaults, fwd_read_redirects(ctx, zones));
832                 fwd_append_config(defaults, fwd_read_rules(ctx, zones));
833                 fwd_append_config(defaults, fwd_read_includes(ctx));
834
835                 ucix_cleanup(ctx);
836
837                 if( (ctx = ucix_init("network")) != NULL )
838                 {
839                         fwd_read_networks(ctx, zones);
840                         ucix_cleanup(ctx);
841
842                         return defaults;
843                 }
844         }
845
846         error:
847         if( ctx ) ucix_cleanup(ctx);
848         fwd_free_config(defaults);
849         fwd_free_config(zones);
850         return NULL;    
851 }
852
853
854 void fwd_free_config(struct fwd_data *h)
855 {
856         struct fwd_data *e = h;
857
858         while( h != NULL )
859         {
860                 e = h->next;
861
862                 switch(h->type)
863                 {
864                         case FWD_S_INCLUDE:
865                                 fwd_free_ptr(h->section.include.path);
866                                 break;
867
868                         case FWD_S_ZONE:
869                                 fwd_free_ptr(h->section.zone.name);
870                                 fwd_free_networks(h->section.zone.networks);
871                                 break;
872
873                         case FWD_S_REDIRECT:
874                                 fwd_free_ptr(h->section.redirect.src_ip);
875                                 fwd_free_ptr(h->section.redirect.src_mac);
876                                 fwd_free_ptr(h->section.redirect.src_port);
877                                 fwd_free_ptr(h->section.redirect.src_dport);
878                                 fwd_free_ptr(h->section.redirect.dest_ip);
879                                 fwd_free_ptr(h->section.redirect.dest_port);
880                                 fwd_free_ptr(h->section.redirect.proto);
881                                 break;
882
883                         case FWD_S_RULE:
884                                 fwd_free_ptr(h->section.rule.src_ip);
885                                 fwd_free_ptr(h->section.rule.src_mac);
886                                 fwd_free_ptr(h->section.rule.src_port);
887                                 fwd_free_ptr(h->section.rule.dest_ip);
888                                 fwd_free_ptr(h->section.rule.dest_port);
889                                 fwd_free_ptr(h->section.rule.proto);
890                                 fwd_free_ptr(h->section.rule.icmp_type);
891                                 break;
892
893                         case FWD_S_DEFAULTS:
894                         case FWD_S_FORWARD:
895                                 /* Make gcc happy */
896                                 break;
897                 }
898
899                 free(h);
900                 h = e;
901         }
902
903         e = h = NULL;
904 }
905
906
907 struct fwd_zone *
908 fwd_lookup_zone(struct fwd_data *h, const char *n)
909 {
910         struct fwd_data *e;
911
912         if( n != NULL )
913         {
914                 for( e = h; e; e = e->next )
915                 {
916                         if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
917                                 return &e->section.zone;
918                 }
919         }
920
921         return NULL;
922 }
923