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