contrib/fwd: reset invert flag after running xt parse handler
[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                         unsigned int i1, i2, i3, i4, i5, i6;
167
168                         if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
169                                 &i1, &i2, &i3, &i4, &i5, &i6) == 6
170                         ) {
171                                 (*m)->mac[0] = (unsigned char)i1;
172                                 (*m)->mac[1] = (unsigned char)i2;
173                                 (*m)->mac[2] = (unsigned char)i3;
174                                 (*m)->mac[3] = (unsigned char)i4;
175                                 (*m)->mac[4] = (unsigned char)i5;
176                                 (*m)->mac[5] = (unsigned char)i6;
177                                 return 0;
178                         }
179                 }
180         }
181
182         fwd_free_ptr(*m);
183         return -1;
184 }
185
186 static int
187 fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
188 {
189         const char *val = ucix_get_option(uci, "firewall", s, o);
190         int min = -1;
191         int max = -1;
192         unsigned int tmp;
193
194         if( val == NULL )
195         {
196                 return 0;
197         }
198         else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
199         {
200                 if( max == -1 )
201                 {
202                         max = min;
203                 }
204                 else if( min > max )
205                 {
206                         tmp = max;
207                         max = min;
208                         min = tmp;
209                 }
210
211                 if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
212                 {
213                         if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
214                         {
215                                 (*p)->min = min;
216                                 (*p)->max = max;
217                                 return 0;
218                         }
219                 }
220         }
221
222         fwd_free_ptr(*p);
223         return -1;
224 }
225
226 static int
227 fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
228 {
229         const char *val = ucix_get_option(uci, "firewall", s, o);
230         int proto;
231
232         if( val == NULL )
233         {
234                 return 0;
235         }
236         else
237         {
238                 if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
239                 {
240                         proto = atoi(val);
241
242                         if( !strcasecmp(val, "all") )
243                         {
244                                 (*p)->type  = FWD_PR_ALL;
245                                 (*p)->proto = 0;
246                         }
247                         else if( !strcasecmp(val, "icmp") )
248                         {
249                                 (*p)->type  = FWD_PR_ICMP;
250                                 (*p)->proto = 0;
251                         }
252                         else if( !strcasecmp(val, "udp") )
253                         {
254                                 (*p)->type  = FWD_PR_UDP;
255                                 (*p)->proto = 0;
256                         }
257                         else if( !strcasecmp(val, "tcp") )
258                         {
259                                 (*p)->type  = FWD_PR_TCP;
260                                 (*p)->proto = 0;
261                         }
262                         else if( !strcasecmp(val, "tcpudp") )
263                         {
264                                 (*p)->type  = FWD_PR_TCPUDP;
265                                 (*p)->proto = 0;
266                         }
267                         else if( proto > 0 )
268                         {
269                                 (*p)->type  = FWD_PR_CUSTOM;
270                                 (*p)->proto = proto;
271                         }
272                         else
273                         {
274                                 goto inval;
275                         }
276
277                         return 0;
278                 }
279         }
280
281         inval:
282         fwd_free_ptr(*p);
283         return -1;
284 }
285
286 static int
287 fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
288 {
289         const char *val = ucix_get_option(uci, "firewall", s, o);
290         unsigned int type, code;
291
292         if( val == NULL )
293         {
294                 return 0;
295         }
296         else
297         {
298                 if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
299                 {
300                         if( sscanf(val, "%u/%u", &type, &code) == 2 )
301                         {
302                                 if( (type > 255) || (code > 255) )
303                                         goto inval;
304
305                                 (*i)->type = type;
306                                 (*i)->code = code;
307
308                                 return 0;
309                         }
310
311                         else if( sscanf(val, "%u", &type) == 1 )
312                         {
313                                 if( type > 255 )
314                                         goto inval;
315
316                                 (*i)->type = type;
317                                 (*i)->code = -1;
318
319                                 return 0;
320                         }
321
322                         /* XXX: no validity check here but I do not want to
323                                 duplicate libipt_icmp.c ... */
324                         else if( sscanf(val, "%31s", (*i)->name) == 1 )
325                         {
326                                 return 0;
327                         }
328                 }
329         }
330
331         inval:
332         fwd_free_ptr(*i);
333         return -1;
334 }
335
336 static const char *
337 fwd_read_string(struct uci_context *uci, const char *s, const char *o)
338 {
339         return ucix_get_option(uci, "firewall", s, o);
340 }
341
342
343 static void
344 fwd_append_config(struct fwd_data *h, struct fwd_data *a)
345 {
346         while( h->next )
347                 h = h->next;
348
349         h->next = a;
350 }
351
352
353 /*
354  * config defaults
355  */
356 static void fwd_read_defaults_cb(
357         struct uci_context *uci,
358         const char *s, struct fwd_defaults *d
359 ) {
360         d->input        = fwd_read_policy(uci, s, "input");
361         d->forward      = fwd_read_policy(uci, s, "forward");
362         d->output       = fwd_read_policy(uci, s, "output");
363         d->syn_flood    = fwd_read_bool(uci, s, "syn_flood", 1);
364         d->syn_rate     = fwd_read_uint(uci, s, "syn_rate", 25);
365         d->syn_burst    = fwd_read_uint(uci, s, "syn_burst", 50);
366         d->drop_invalid = fwd_read_bool(uci, s, "drop_invalid", 1);
367 }
368
369 static struct fwd_data *
370 fwd_read_defaults(struct uci_context *uci)
371 {
372         struct fwd_data *dt;
373         struct fwd_defaults d;
374
375         if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
376         {
377                 memset(&d, 0, sizeof(d));
378
379                 ucix_for_each_section_type(uci, "firewall", "defaults",
380                         (void *)fwd_read_defaults_cb, &d);
381
382                 memcpy(&dt->section.defaults, &d, sizeof(d));
383
384                 dt->type = FWD_S_DEFAULTS;
385                 dt->next = NULL;
386
387                 return dt;
388         }
389
390         return NULL;
391 }
392
393
394 /*
395  * config zone
396  */
397 static void fwd_read_zone_networks_cb(
398         const char *net, struct fwd_network_list **np
399 ) {
400         struct fwd_network_list *nn;
401
402         if( (nn = fwd_alloc_ptr(struct fwd_network_list)) != NULL )
403         {
404                 nn->name = strdup(net);
405                 nn->next = *np;
406                 *np = nn;
407         }
408 }
409
410 static void fwd_read_zones_cb(
411         struct uci_context *uci,
412         const char *s, struct fwd_data_conveyor *cv
413 ) {
414         struct fwd_data *dtn;
415         struct fwd_network_list *net = NULL;
416         const char *name;
417
418         if( !(name = fwd_read_string(uci, s, "name")) )
419                 fwd_read_error("section '%s' is missing 'name' option!", s);
420
421         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
422         {
423                 dtn->section.zone.name      = strdup(name);
424                 dtn->section.zone.masq      = fwd_read_bool(uci, s, "masq", 0);
425                 dtn->section.zone.mtu_fix   = fwd_read_bool(uci, s, "mtu_fix", 0);
426                 dtn->section.zone.conntrack = fwd_read_bool(uci, s, "conntrack", 0);
427
428                 dtn->section.zone.input     = fwd_read_policy(uci, s, "input")
429                         ?: cv->head->section.defaults.input   ?: FWD_P_DROP;
430
431                 dtn->section.zone.forward   = fwd_read_policy(uci, s, "forward")
432                         ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
433
434                 dtn->section.zone.output    = fwd_read_policy(uci, s, "output")
435                         ?: cv->head->section.defaults.output  ?: FWD_P_DROP;
436
437                 /* try to parse option/list network ... */
438                 if( ucix_for_each_list(uci, "firewall", s, "network",
439                         (void *)&fwd_read_zone_networks_cb, &net) < 0 )
440                 {
441                         /* ... didn't work, fallback to option name */
442                         fwd_read_zone_networks_cb(name, &net);
443                 }
444
445                 dtn->section.zone.networks = net;
446                 dtn->type = FWD_S_ZONE;
447                 dtn->next = cv->cursor;
448                 cv->cursor = dtn;
449         }
450 }
451
452 static struct fwd_data *
453 fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
454 {
455         struct fwd_data_conveyor cv;
456
457         cv.cursor = NULL;
458         cv.head = def;
459
460         ucix_for_each_section_type(uci, "firewall", "zone",
461                 (void *)fwd_read_zones_cb, &cv);
462
463         return cv.cursor;
464 }
465
466
467 /*
468  * config forwarding
469  */
470 static void fwd_read_forwards_cb(
471         struct uci_context *uci,
472         const char *s, struct fwd_data_conveyor *cv
473 ) {
474         const char *src, *dest;
475         struct fwd_data *dtn;
476         struct fwd_zone *zsrc  = NULL;
477         struct fwd_zone *zdest = NULL;
478
479         if( !(src = fwd_read_string(uci, s, "src")) )
480                 fwd_read_error("section '%s' is missing 'src' option!", s);
481         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
482                 fwd_read_error("section '%s' references unknown src zone '%s'!", s, src);
483         else if( !(dest = fwd_read_string(uci, s, "dest")) )
484                 fwd_read_error("section '%s' is missing 'dest' option!", s);
485         else if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
486                 fwd_read_error("section '%s' references unknown dest zone '%s'!", s, dest);
487         
488         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
489         {
490                 dtn->section.forwarding.src = zsrc;
491                 dtn->section.forwarding.dest = zdest;
492                 dtn->section.forwarding.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
493                 dtn->section.forwarding.masq = fwd_read_bool(uci, s, "masq", 0);
494
495                 dtn->type = FWD_S_FORWARD;
496
497                 if( zsrc )
498                 {
499                         dtn->next = zsrc->forwardings;
500                         zsrc->forwardings = dtn;
501                 }
502                 else
503                 {
504                         dtn->next = cv->cursor;
505                         cv->cursor = dtn;
506                 }
507         }
508         else
509         {
510                 fwd_read_error("out of memory while parsing config!");
511         }
512 }
513
514 static struct fwd_data *
515 fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
516 {
517         struct fwd_data_conveyor cv;
518
519         cv.cursor = NULL;
520         cv.head = zones;
521
522         ucix_for_each_section_type(uci, "firewall", "forwarding",
523                 (void *)fwd_read_forwards_cb, &cv);
524
525         return cv.cursor;
526 }
527
528
529 /*
530  * config redirect
531  */
532 static void fwd_read_redirects_cb(
533         struct uci_context *uci,
534         const char *s, struct fwd_data_conveyor *cv
535 ) {
536         const char *src;
537         struct fwd_data *dtn  = NULL;
538         struct fwd_data *dtn2 = NULL;
539         struct fwd_zone *zsrc = NULL;
540
541         /* check zone */
542         if( !(src = fwd_read_string(uci, s, "src")) )
543                 fwd_read_error(
544                         "section '%s' is missing 'src' option!",
545                         s
546                 );
547
548         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
549                 fwd_read_error(
550                         "section '%s' references unknown src zone '%s'!",
551                         s, src
552                 );
553
554         /* uci context, section, name, type */
555         fwd_check_option(uci, s, src_ip, cidr);
556         fwd_check_option(uci, s, src_mac, mac);
557         fwd_check_option(uci, s, src_port, portrange);
558         fwd_check_option(uci, s, src_dport, portrange);
559         fwd_check_option(uci, s, dest_ip, cidr);
560         fwd_check_option(uci, s, dest_port, portrange);
561         fwd_check_option(uci, s, proto, proto);
562         
563         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
564         {
565                 dtn->section.redirect.proto     = proto;
566                 dtn->section.redirect.src       = zsrc;
567                 dtn->section.redirect.src_ip    = src_ip;
568                 dtn->section.redirect.src_mac   = src_mac;
569                 dtn->section.redirect.src_port  = src_port;
570                 dtn->section.redirect.src_dport = src_dport;
571                 dtn->section.redirect.dest_ip   = dest_ip;
572                 dtn->section.redirect.dest_port = dest_port;
573
574                 dtn->type = FWD_S_REDIRECT;
575                 dtn->next = zsrc->redirects;
576                 zsrc->redirects = dtn;
577
578                 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
579                 {
580                         if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
581                             !(dtn2->section.redirect.proto = fwd_alloc_ptr(struct fwd_proto))
582                         ) {
583                                 fwd_free_ptr(dtn2);
584                                 fwd_read_error("out of memory while parsing config!");
585                         }
586
587                         dtn->section.redirect.proto->type = FWD_PR_UDP;
588                         dtn2->section.redirect.proto->type = FWD_PR_TCP;
589
590                         dtn2->section.redirect.src       = zsrc;
591                         dtn2->section.redirect.src_ip    = src_ip;
592                         dtn2->section.redirect.src_mac   = src_mac;
593                         dtn2->section.redirect.src_port  = src_port;
594                         dtn2->section.redirect.src_dport = src_dport;
595                         dtn2->section.redirect.dest_ip   = dest_ip;
596                         dtn2->section.redirect.dest_port = dest_port;
597                         dtn2->section.redirect.clone     = 1;
598
599                         dtn2->type = FWD_S_REDIRECT;
600                         dtn2->next = zsrc->redirects;
601                         zsrc->redirects = dtn2;
602                 }
603         }
604         else
605         {
606                 fwd_read_error("out of memory while parsing config!");
607         }
608 }
609
610 static struct fwd_data *
611 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
612 {
613         struct fwd_data_conveyor cv;
614
615         cv.cursor = NULL;
616         cv.head = zones;
617
618         ucix_for_each_section_type(uci, "firewall", "redirect",
619                 (void *)fwd_read_redirects_cb, &cv);
620
621         return cv.cursor;
622 }
623
624
625 /*
626  * config rule
627  */
628 static void fwd_read_rules_cb(
629         struct uci_context *uci,
630         const char *s, struct fwd_data_conveyor *cv
631 ) {
632         const char *src, *dest;
633         struct fwd_data *dtn   = NULL;
634         struct fwd_data *dtn2  = NULL;
635         struct fwd_zone *zsrc  = NULL;
636         struct fwd_zone *zdest = NULL;
637
638         /* check zones */
639         if( !(src = fwd_read_string(uci, s, "src")) )
640                 fwd_read_error(
641                         "section '%s' is missing 'src' option!",
642                         s
643                 );
644
645         else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
646                 fwd_read_error(
647                         "section '%s' references unknown src zone '%s'!",
648                         s, src
649                 );
650
651         if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
652                 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
653                         fwd_read_error(
654                                 "section '%s' references unknown dest zone '%s'!",
655                                 s, dest
656                         );
657
658         /* uci context, section, name, type */
659         fwd_check_option(uci, s, src_ip, cidr);
660         fwd_check_option(uci, s, src_mac, mac);
661         fwd_check_option(uci, s, src_port, portrange);
662         fwd_check_option(uci, s, dest_ip, cidr);
663         fwd_check_option(uci, s, dest_port, portrange);
664         fwd_check_option(uci, s, proto, proto);
665         fwd_check_option(uci, s, icmptype, icmptype);
666         
667         if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
668         {
669                 dtn->section.rule.proto     = proto;
670                 dtn->section.rule.icmp_type = icmptype;
671                 dtn->section.rule.src       = zsrc;
672                 dtn->section.rule.src_ip    = src_ip;
673                 dtn->section.rule.src_mac   = src_mac;
674                 dtn->section.rule.src_port  = src_port;
675                 dtn->section.rule.dest      = zdest;
676                 dtn->section.rule.dest_ip   = dest_ip;
677                 dtn->section.rule.dest_port = dest_port;
678                 dtn->section.rule.target    = fwd_read_policy(uci, s, "target");
679
680                 dtn->type = FWD_S_RULE;
681                 dtn->next = zsrc->rules;
682                 zsrc->rules = dtn;
683
684                 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
685                 {
686                         if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
687                             !(dtn2->section.rule.proto = fwd_alloc_ptr(struct fwd_proto))
688                         ) {
689                                 fwd_free_ptr(dtn2);
690                                 fwd_read_error("out of memory while parsing config!");
691                         }
692
693                         dtn->section.rule.proto->type = FWD_PR_UDP;
694                         dtn2->section.rule.proto->type = FWD_PR_TCP;
695
696                         dtn2->section.rule.src       = zsrc;
697                         dtn2->section.rule.src_ip    = src_ip;
698                         dtn2->section.rule.src_mac   = src_mac;
699                         dtn2->section.rule.src_port  = src_port;
700                         dtn2->section.rule.dest      = zdest;
701                         dtn2->section.rule.dest_ip   = dest_ip;
702                         dtn2->section.rule.dest_port = dest_port;
703                         dtn2->section.rule.target    = dtn->section.rule.target;
704                         dtn2->section.rule.clone     = 1;
705
706                         dtn2->type = FWD_S_RULE;
707                         dtn2->next = zsrc->rules;
708                         zsrc->rules = dtn2;
709                 }
710         }
711         else
712         {
713                 fwd_read_error("out of memory while parsing config!");
714         }
715 }
716
717 static struct fwd_data *
718 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
719 {
720         struct fwd_data_conveyor cv;
721
722         cv.cursor = NULL;
723         cv.head = zones;
724
725         ucix_for_each_section_type(uci, "firewall", "rule",
726                 (void *)fwd_read_rules_cb, &cv);
727
728         return cv.cursor;
729 }
730
731
732 /*
733  * config include
734  */
735 static void fwd_read_includes_cb(
736         struct uci_context *uci,
737         const char *s, struct fwd_data_conveyor *cv
738 ) {
739         const char *path = fwd_read_string(uci, s, "path");
740         struct fwd_data *dtn = NULL;
741
742         if( path != NULL )
743         {
744                 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
745                 {
746                         dtn->section.include.path = strdup(path);
747
748                         dtn->type = FWD_S_INCLUDE;
749                         dtn->next = cv->cursor;
750                         cv->cursor = dtn;
751                 }
752                 else
753                 {
754                         fwd_read_error("out of memory while parsing config!");
755                 }
756         }
757 }
758
759 static struct fwd_data *
760 fwd_read_includes(struct uci_context *uci)
761 {
762         struct fwd_data_conveyor cv;
763
764         cv.cursor = NULL;
765         cv.head   = NULL;
766
767         ucix_for_each_section_type(uci, "firewall", "include",
768                 (void *)fwd_read_includes_cb, &cv);
769
770         return cv.cursor;
771 }
772
773
774 /*
775  * config interface
776  */
777 static void fwd_read_network_data(
778         struct uci_context *uci, struct fwd_network_list *net
779 ) {
780         struct fwd_network_list *e;
781         const char *type, *ifname;
782
783         for( e = net; e; e = e->next )
784         {
785                 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
786                 {
787                         if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
788                                 fwd_read_error(
789                                         "section '%s' is missing 'ifname' option!",
790                                         e->name
791                                 );
792
793                         e->isalias = (strcmp(type, "alias") ? 0 : 1);
794                         e->ifname  = strdup(ifname);
795                 }
796         }
797 }
798
799 static void fwd_read_networks(
800         struct uci_context *uci, struct fwd_data *zones
801 ) {
802         struct fwd_data *e;
803
804         for( e = zones; e; e = e->next )
805                 if( e->type == FWD_S_ZONE )
806                         fwd_read_network_data(uci, e->section.zone.networks);
807 }
808
809 static void fwd_free_networks(struct fwd_network_list *h)
810 {
811         struct fwd_network_list *e = h;
812
813         while( h != NULL )
814         {
815                 e = h->next;
816
817                 fwd_free_ptr(h->name);
818                 fwd_free_ptr(h->ifname);
819                 fwd_free_ptr(h->addr);
820
821                 free(h);
822                 h = e;
823         }
824
825         e = h = NULL;
826 }
827
828
829
830 struct fwd_data * fwd_read_config(void)
831 {
832         struct uci_context *ctx;
833         struct fwd_data *defaults, *zones;
834
835         if( (ctx = ucix_init("firewall")) != NULL )
836         {
837                 if( !(defaults = fwd_read_defaults(ctx)) )
838                         goto error;
839
840                 if( !(zones = fwd_read_zones(ctx, defaults)) )
841                         goto error;
842
843                 fwd_append_config(defaults, zones);
844                 fwd_append_config(defaults, fwd_read_forwards(ctx, zones));
845                 fwd_append_config(defaults, fwd_read_redirects(ctx, zones));
846                 fwd_append_config(defaults, fwd_read_rules(ctx, zones));
847                 fwd_append_config(defaults, fwd_read_includes(ctx));
848
849                 ucix_cleanup(ctx);
850
851                 if( (ctx = ucix_init("network")) != NULL )
852                 {
853                         fwd_read_networks(ctx, zones);
854                         ucix_cleanup(ctx);
855
856                         return defaults;
857                 }
858         }
859
860         error:
861         if( ctx ) ucix_cleanup(ctx);
862         fwd_free_config(defaults);
863         fwd_free_config(zones);
864         return NULL;    
865 }
866
867
868 void fwd_free_config(struct fwd_data *h)
869 {
870         struct fwd_data *e = h;
871
872         while( h != NULL )
873         {
874                 e = h->next;
875
876                 switch(h->type)
877                 {
878                         case FWD_S_INCLUDE:
879                                 fwd_free_ptr(h->section.include.path);
880                                 break;
881
882                         case FWD_S_ZONE:
883                                 fwd_free_ptr(h->section.zone.name);
884                                 fwd_free_networks(h->section.zone.networks);
885                                 fwd_free_config(h->section.zone.rules);
886                                 fwd_free_config(h->section.zone.redirects);
887                                 fwd_free_config(h->section.zone.forwardings);
888                                 break;
889
890                         case FWD_S_REDIRECT:
891                                 /* Clone rules share all pointers except proto.
892                    Prevent a double-free here */          
893                                 if( ! h->section.redirect.clone )
894                                 {
895                                         fwd_free_ptr(h->section.redirect.src_ip);
896                                         fwd_free_ptr(h->section.redirect.src_mac);
897                                         fwd_free_ptr(h->section.redirect.src_port);
898                                         fwd_free_ptr(h->section.redirect.src_dport);
899                                         fwd_free_ptr(h->section.redirect.dest_ip);
900                                         fwd_free_ptr(h->section.redirect.dest_port);
901                                 }
902                                 fwd_free_ptr(h->section.redirect.proto);
903                                 break;
904
905                         case FWD_S_RULE:
906                                 /* Clone rules share all pointers except proto.
907                    Prevent a double-free here */          
908                                 if( ! h->section.rule.clone )
909                                 {
910                                         fwd_free_ptr(h->section.rule.src_ip);
911                                         fwd_free_ptr(h->section.rule.src_mac);
912                                         fwd_free_ptr(h->section.rule.src_port);
913                                         fwd_free_ptr(h->section.rule.dest_ip);
914                                         fwd_free_ptr(h->section.rule.dest_port);
915                                         fwd_free_ptr(h->section.rule.icmp_type);
916                                 }
917                                 fwd_free_ptr(h->section.rule.proto);
918                                 break;
919
920                         case FWD_S_DEFAULTS:
921                         case FWD_S_FORWARD:
922                                 /* Make gcc happy */
923                                 break;
924                 }
925
926                 fwd_free_ptr(h);
927                 h = e;
928         }
929
930         e = h = NULL;
931 }
932
933
934 struct fwd_zone *
935 fwd_lookup_zone(struct fwd_data *h, const char *n)
936 {
937         struct fwd_data *e;
938
939         if( n != NULL )
940         {
941                 for( e = h; e; e = e->next )
942                 {
943                         if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
944                                 return &e->section.zone;
945                 }
946         }
947
948         return NULL;
949 }
950