contrib/fwd: rewrite rule generate to use xtables api
[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