2 * fwd - OpenWrt firewall daemon - config parsing
4 * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
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.
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.
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/.
22 #include "fwd_config.h"
27 #define fwd_read_error(...) do { \
28 fprintf(stderr, "ERROR: "); \
29 fprintf(stderr, __VA_ARGS__); \
30 fprintf(stderr, "\n"); \
39 fwd_read_policy(struct uci_context *uci, const char *s, const char *o)
41 const char *val = ucix_get_option(uci, "firewall", s, o);
65 fwd_read_bool(struct uci_context *uci, const char *s, const char *o, int d)
67 const char *val = ucix_get_option(uci, "firewall", s, o);
71 if( !strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1") )
81 fwd_read_uint(struct uci_context *uci, const char *s, const char *o, unsigned int d)
83 const char *val = ucix_get_option(uci, "firewall", s, o);
94 fwd_read_cidr(struct uci_context *uci, const char *s, const char *o, struct fwd_cidr **c)
96 const char *val = ucix_get_option(uci, "firewall", s, o);
97 char ip[32], prefix[32];
101 memset(prefix, 0, 32);
107 else if( (strlen(val) < 32) && (sscanf(val, "%[^/]/%s", ip, prefix) > 0) )
109 if( !(*c = fwd_alloc_ptr(struct fwd_cidr)) )
112 if( inet_aton(ip, &ina) )
114 (*c)->addr.s_addr = ina.s_addr;
116 if( strchr(prefix, '.') )
118 if( inet_aton(prefix, &ina) )
121 ina.s_addr = ntohl(ina.s_addr);
123 while( !(ina.s_addr & 1) )
136 (*c)->prefix = prefix[0] ? atoi(prefix) : 32;
138 if( ((*c)->prefix < 0) || ((*c)->prefix > 32) )
154 fwd_read_mac(struct uci_context *uci, const char *s, const char *o, struct fwd_mac **m)
156 const char *val = ucix_get_option(uci, "firewall", s, o);
164 if( (*m = fwd_alloc_ptr(struct fwd_mac)) != NULL )
166 if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
167 (unsigned int *)&(*m)->mac[0], (unsigned int *)&(*m)->mac[1],
168 (unsigned int *)&(*m)->mac[2], (unsigned int *)&(*m)->mac[3],
169 (unsigned int *)&(*m)->mac[4], (unsigned int *)&(*m)->mac[5]) == 6
181 fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
183 const char *val = ucix_get_option(uci, "firewall", s, o);
192 else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
205 if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
207 if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
221 fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
223 const char *val = ucix_get_option(uci, "firewall", s, o);
232 if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
236 if( !strcasecmp(val, "all") )
238 (*p)->type = FWD_PR_ALL;
241 else if( !strcasecmp(val, "icmp") )
243 (*p)->type = FWD_PR_ICMP;
246 else if( !strcasecmp(val, "udp") )
248 (*p)->type = FWD_PR_UDP;
251 else if( !strcasecmp(val, "tcp") )
253 (*p)->type = FWD_PR_TCP;
256 else if( !strcasecmp(val, "tcpudp") )
258 (*p)->type = FWD_PR_TCPUDP;
263 (*p)->type = FWD_PR_CUSTOM;
281 fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
283 const char *val = ucix_get_option(uci, "firewall", s, o);
284 unsigned int type, code;
292 if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
294 if( sscanf(val, "%u/%u", &type, &code) == 2 )
296 if( (type > 255) || (code > 255) )
305 else if( sscanf(val, "%u", &type) == 1 )
316 /* XXX: no validity check here but I do not want to
317 duplicate libipt_icmp.c ... */
318 else if( sscanf(val, "%31s", (*i)->name) == 1 )
331 fwd_read_string(struct uci_context *uci, const char *s, const char *o)
333 return ucix_get_option(uci, "firewall", s, o);
338 fwd_append_config(struct fwd_data *h, struct fwd_data *a)
350 static void fwd_read_defaults_cb(
351 struct uci_context *uci,
352 const char *s, struct fwd_defaults *d
354 d->input = fwd_read_policy(uci, s, "input");
355 d->forward = fwd_read_policy(uci, s, "forward");
356 d->output = fwd_read_policy(uci, s, "output");
357 d->syn_flood = fwd_read_bool(uci, s, "syn_flood", 1);
358 d->syn_rate = fwd_read_uint(uci, s, "syn_rate", 25);
359 d->syn_burst = fwd_read_uint(uci, s, "syn_burst", 50);
360 d->drop_invalid = fwd_read_bool(uci, s, "drop_invalid", 1);
363 static struct fwd_data *
364 fwd_read_defaults(struct uci_context *uci)
367 struct fwd_defaults d;
369 if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
371 memset(&d, 0, sizeof(d));
373 ucix_for_each_section_type(uci, "firewall", "defaults",
374 (void *)fwd_read_defaults_cb, &d);
376 memcpy(&dt->section.defaults, &d, sizeof(d));
378 dt->type = FWD_S_DEFAULTS;
391 static void fwd_read_zone_networks_cb(
392 const char *net, struct fwd_network_list **np
394 struct fwd_network_list *nn;
396 if( (nn = fwd_alloc_ptr(struct fwd_network_list)) != NULL )
398 nn->name = strdup(net);
404 static void fwd_read_zones_cb(
405 struct uci_context *uci,
406 const char *s, struct fwd_data_conveyor *cv
408 struct fwd_data *dtn;
409 struct fwd_network_list *net = NULL;
412 if( !(name = fwd_read_string(uci, s, "name")) )
413 fwd_read_error("section '%s' is missing 'name' option!", s);
415 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
417 dtn->section.zone.name = strdup(name);
418 dtn->section.zone.masq = fwd_read_bool(uci, s, "masq", 0);
419 dtn->section.zone.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
420 dtn->section.zone.conntrack = fwd_read_bool(uci, s, "conntrack", 0);
422 dtn->section.zone.input = fwd_read_policy(uci, s, "input")
423 ?: cv->head->section.defaults.input ?: FWD_P_DROP;
425 dtn->section.zone.forward = fwd_read_policy(uci, s, "forward")
426 ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
428 dtn->section.zone.output = fwd_read_policy(uci, s, "output")
429 ?: cv->head->section.defaults.output ?: FWD_P_DROP;
431 /* try to parse option/list network ... */
432 if( ucix_for_each_list(uci, "firewall", s, "network",
433 (void *)&fwd_read_zone_networks_cb, &net) < 0 )
435 /* ... didn't work, fallback to option name */
436 fwd_read_zone_networks_cb(name, &net);
439 dtn->section.zone.networks = net;
440 dtn->type = FWD_S_ZONE;
441 dtn->next = cv->cursor;
446 static struct fwd_data *
447 fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
449 struct fwd_data_conveyor cv;
454 ucix_for_each_section_type(uci, "firewall", "zone",
455 (void *)fwd_read_zones_cb, &cv);
464 static void fwd_read_forwards_cb(
465 struct uci_context *uci,
466 const char *s, struct fwd_data_conveyor *cv
468 const char *src, *dest;
469 struct fwd_data *dtn;
470 struct fwd_zone *zsrc = NULL;
471 struct fwd_zone *zdest = NULL;
473 if( (src = fwd_read_string(uci, s, "src")) != NULL )
475 if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
476 fwd_read_error("section '%s' references unknown src zone '%s'!", s, src);
479 if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
481 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
482 fwd_read_error("section '%s' references unknown dest zone '%s'!", s, dest);
485 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
487 dtn->section.forwarding.src = zsrc;
488 dtn->section.forwarding.dest = zdest;
489 dtn->section.forwarding.mtu_fix = fwd_read_bool(uci, s, "mtu_fix", 0);
490 dtn->section.forwarding.masq = fwd_read_bool(uci, s, "masq", 0);
492 dtn->type = FWD_S_FORWARD;
493 dtn->next = cv->cursor;
498 fwd_read_error("out of memory while parsing config!");
502 static struct fwd_data *
503 fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
505 struct fwd_data_conveyor cv;
510 ucix_for_each_section_type(uci, "firewall", "forwarding",
511 (void *)fwd_read_forwards_cb, &cv);
520 static void fwd_read_redirects_cb(
521 struct uci_context *uci,
522 const char *s, struct fwd_data_conveyor *cv
525 struct fwd_data *dtn = NULL;
526 struct fwd_zone *zsrc = NULL;
529 if( !(src = fwd_read_string(uci, s, "src")) )
531 "section '%s' is missing 'src' option!",
535 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
537 "section '%s' references unknown src zone '%s'!",
541 /* uci context, section, name, type */
542 fwd_check_option(uci, s, src_ip, cidr);
543 fwd_check_option(uci, s, src_mac, mac);
544 fwd_check_option(uci, s, src_port, portrange);
545 fwd_check_option(uci, s, src_dport, portrange);
546 fwd_check_option(uci, s, dest_ip, cidr);
547 fwd_check_option(uci, s, dest_port, portrange);
548 fwd_check_option(uci, s, proto, proto);
550 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
552 dtn->section.redirect.proto = proto;
553 dtn->section.redirect.src = zsrc;
554 dtn->section.redirect.src_ip = src_ip;
555 dtn->section.redirect.src_mac = src_mac;
556 dtn->section.redirect.src_port = src_port;
557 dtn->section.redirect.src_dport = src_dport;
558 dtn->section.redirect.dest_ip = dest_ip;
559 dtn->section.redirect.dest_port = dest_port;
561 dtn->type = FWD_S_REDIRECT;
562 dtn->next = cv->cursor;
567 fwd_read_error("out of memory while parsing config!");
571 static struct fwd_data *
572 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
574 struct fwd_data_conveyor cv;
579 ucix_for_each_section_type(uci, "firewall", "redirect",
580 (void *)fwd_read_redirects_cb, &cv);
589 static void fwd_read_rules_cb(
590 struct uci_context *uci,
591 const char *s, struct fwd_data_conveyor *cv
593 const char *src, *dest;
594 struct fwd_data *dtn = NULL;
595 struct fwd_zone *zsrc = NULL;
596 struct fwd_zone *zdest = NULL;
599 if( !(src = fwd_read_string(uci, s, "src")) )
601 "section '%s' is missing 'src' option!",
605 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
607 "section '%s' references unknown src zone '%s'!",
611 if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
612 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
614 "section '%s' references unknown dest zone '%s'!",
618 /* uci context, section, name, type */
619 fwd_check_option(uci, s, src_ip, cidr);
620 fwd_check_option(uci, s, src_mac, mac);
621 fwd_check_option(uci, s, src_port, portrange);
622 fwd_check_option(uci, s, dest_ip, cidr);
623 fwd_check_option(uci, s, dest_port, portrange);
624 fwd_check_option(uci, s, proto, proto);
625 fwd_check_option(uci, s, icmptype, icmptype);
627 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
629 dtn->section.rule.proto = proto;
630 dtn->section.rule.icmp_type = icmptype;
631 dtn->section.rule.src = zsrc;
632 dtn->section.rule.src_ip = src_ip;
633 dtn->section.rule.src_mac = src_mac;
634 dtn->section.rule.src_port = src_port;
635 dtn->section.rule.dest = zdest;
636 dtn->section.rule.dest_ip = dest_ip;
637 dtn->section.rule.dest_port = dest_port;
638 dtn->section.rule.target = fwd_read_policy(uci, s, "target");
640 dtn->type = FWD_S_RULE;
641 dtn->next = cv->cursor;
646 fwd_read_error("out of memory while parsing config!");
650 static struct fwd_data *
651 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
653 struct fwd_data_conveyor cv;
658 ucix_for_each_section_type(uci, "firewall", "rule",
659 (void *)fwd_read_rules_cb, &cv);
668 static void fwd_read_includes_cb(
669 struct uci_context *uci,
670 const char *s, struct fwd_data_conveyor *cv
672 const char *path = fwd_read_string(uci, s, "path");
673 struct fwd_data *dtn = NULL;
677 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
679 dtn->section.include.path = strdup(path);
681 dtn->type = FWD_S_INCLUDE;
682 dtn->next = cv->cursor;
687 fwd_read_error("out of memory while parsing config!");
692 static struct fwd_data *
693 fwd_read_includes(struct uci_context *uci)
695 struct fwd_data_conveyor cv;
700 ucix_for_each_section_type(uci, "firewall", "include",
701 (void *)fwd_read_includes_cb, &cv);
710 static void fwd_read_network_data(
711 struct uci_context *uci, struct fwd_network_list *net
713 struct fwd_network_list *e;
714 const char *type, *ifname;
716 for( e = net; e; e = e->next )
718 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
720 if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
722 "section '%s' is missing 'ifname' option!",
726 e->isalias = (strcmp(type, "alias") ? 0 : 1);
727 e->ifname = strdup(ifname);
732 static void fwd_read_networks(
733 struct uci_context *uci, struct fwd_data *zones
737 for( e = zones; e; e = e->next )
738 if( e->type == FWD_S_ZONE )
739 fwd_read_network_data(uci, e->section.zone.networks);
742 static void fwd_free_networks(struct fwd_network_list *h)
744 struct fwd_network_list *e = h;
750 fwd_free_ptr(h->name);
751 fwd_free_ptr(h->ifname);
752 fwd_free_ptr(h->addr);
763 struct fwd_data * fwd_read_config(void)
765 struct uci_context *ctx;
766 struct fwd_data *defaults, *zones;
768 if( (ctx = ucix_init("firewall")) != NULL )
770 if( !(defaults = fwd_read_defaults(ctx)) )
773 if( !(zones = fwd_read_zones(ctx, defaults)) )
776 fwd_append_config(defaults, zones);
777 fwd_append_config(defaults, fwd_read_forwards(ctx, zones));
778 fwd_append_config(defaults, fwd_read_redirects(ctx, zones));
779 fwd_append_config(defaults, fwd_read_rules(ctx, zones));
780 fwd_append_config(defaults, fwd_read_includes(ctx));
784 if( (ctx = ucix_init("network")) != NULL )
786 fwd_read_networks(ctx, zones);
794 if( ctx ) ucix_cleanup(ctx);
795 fwd_free_config(defaults);
796 fwd_free_config(zones);
801 void fwd_free_config(struct fwd_data *h)
803 struct fwd_data *e = h;
812 fwd_free_ptr(h->section.include.path);
816 fwd_free_ptr(h->section.zone.name);
817 fwd_free_networks(h->section.zone.networks);
821 fwd_free_ptr(h->section.redirect.src_ip);
822 fwd_free_ptr(h->section.redirect.src_mac);
823 fwd_free_ptr(h->section.redirect.src_port);
824 fwd_free_ptr(h->section.redirect.src_dport);
825 fwd_free_ptr(h->section.redirect.dest_ip);
826 fwd_free_ptr(h->section.redirect.dest_port);
827 fwd_free_ptr(h->section.redirect.proto);
831 fwd_free_ptr(h->section.rule.src_ip);
832 fwd_free_ptr(h->section.rule.src_mac);
833 fwd_free_ptr(h->section.rule.src_port);
834 fwd_free_ptr(h->section.rule.dest_ip);
835 fwd_free_ptr(h->section.rule.dest_port);
836 fwd_free_ptr(h->section.rule.proto);
837 fwd_free_ptr(h->section.rule.icmp_type);
855 fwd_lookup_zone(struct fwd_data *h, const char *n)
861 for( e = h; e; e = e->next )
863 if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
864 return &e->section.zone;