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