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"
23 #include "fwd_utils.h"
28 #define fwd_read_error(...) do { \
29 fprintf(stderr, "ERROR: "); \
30 fprintf(stderr, __VA_ARGS__); \
31 fprintf(stderr, "\n"); \
40 fwd_read_policy(struct uci_context *uci, const char *s, const char *o)
42 const char *val = ucix_get_option(uci, "firewall", s, o);
66 fwd_read_bool(struct uci_context *uci, const char *s, const char *o, int d)
68 const char *val = ucix_get_option(uci, "firewall", s, o);
72 if( !strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1") )
82 fwd_read_uint(struct uci_context *uci, const char *s, const char *o, unsigned int d)
84 const char *val = ucix_get_option(uci, "firewall", s, o);
95 fwd_read_cidr(struct uci_context *uci, const char *s, const char *o, struct fwd_cidr **c)
97 const char *val = ucix_get_option(uci, "firewall", s, o);
98 char ip[32], prefix[32];
102 memset(prefix, 0, 32);
108 else if( (strlen(val) < 32) && (sscanf(val, "%[^/]/%s", ip, prefix) > 0) )
110 if( !(*c = fwd_alloc_ptr(struct fwd_cidr)) )
113 if( inet_aton(ip, &ina) )
115 (*c)->addr.s_addr = ina.s_addr;
117 if( strchr(prefix, '.') )
119 if( inet_aton(prefix, &ina) )
122 ina.s_addr = ntohl(ina.s_addr);
124 while( !(ina.s_addr & 1) )
137 (*c)->prefix = prefix[0] ? atoi(prefix) : 32;
139 if( ((*c)->prefix < 0) || ((*c)->prefix > 32) )
155 fwd_read_mac(struct uci_context *uci, const char *s, const char *o, struct fwd_mac **m)
157 const char *val = ucix_get_option(uci, "firewall", s, o);
165 if( (*m = fwd_alloc_ptr(struct fwd_mac)) != NULL )
167 unsigned int i1, i2, i3, i4, i5, i6;
169 if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
170 &i1, &i2, &i3, &i4, &i5, &i6) == 6
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;
188 fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
190 const char *val = ucix_get_option(uci, "firewall", s, o);
199 else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
212 if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
214 if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
228 fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
230 const char *val = ucix_get_option(uci, "firewall", s, o);
239 if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
243 if( !strcasecmp(val, "all") )
245 (*p)->type = FWD_PR_ALL;
248 else if( !strcasecmp(val, "icmp") )
250 (*p)->type = FWD_PR_ICMP;
253 else if( !strcasecmp(val, "udp") )
255 (*p)->type = FWD_PR_UDP;
258 else if( !strcasecmp(val, "tcp") )
260 (*p)->type = FWD_PR_TCP;
263 else if( !strcasecmp(val, "tcpudp") )
265 (*p)->type = FWD_PR_TCPUDP;
270 (*p)->type = FWD_PR_CUSTOM;
288 fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
290 const char *val = ucix_get_option(uci, "firewall", s, o);
291 unsigned int type, code;
299 if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
301 if( sscanf(val, "%u/%u", &type, &code) == 2 )
303 if( (type > 255) || (code > 255) )
312 else if( sscanf(val, "%u", &type) == 1 )
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 )
338 fwd_read_string(struct uci_context *uci, const char *s, const char *o)
340 return ucix_get_option(uci, "firewall", s, o);
345 fwd_append_config(struct fwd_data *h, struct fwd_data *a)
357 static void fwd_read_defaults_cb(
358 struct uci_context *uci,
359 const char *s, struct fwd_defaults *d
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);
370 static struct fwd_data *
371 fwd_read_defaults(struct uci_context *uci)
374 struct fwd_defaults d;
376 if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
378 memset(&d, 0, sizeof(d));
380 ucix_for_each_section_type(uci, "firewall", "defaults",
381 (void *)fwd_read_defaults_cb, &d);
383 memcpy(&dt->section.defaults, &d, sizeof(d));
385 dt->type = FWD_S_DEFAULTS;
398 static void fwd_read_zone_networks_cb(
399 const char *net, struct fwd_network **np
401 struct fwd_network *nn;
403 if( (nn = fwd_alloc_ptr(struct fwd_network)) != NULL )
405 nn->name = strdup(net);
411 static void fwd_read_zones_cb(
412 struct uci_context *uci,
413 const char *s, struct fwd_data_conveyor *cv
415 struct fwd_data *dtn;
416 struct fwd_network *net = NULL;
419 if( !(name = fwd_read_string(uci, s, "name")) )
420 fwd_read_error("section '%s' is missing 'name' option!", s);
422 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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);
429 dtn->section.zone.input = fwd_read_policy(uci, s, "input")
430 ?: cv->head->section.defaults.input ?: FWD_P_DROP;
432 dtn->section.zone.forward = fwd_read_policy(uci, s, "forward")
433 ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
435 dtn->section.zone.output = fwd_read_policy(uci, s, "output")
436 ?: cv->head->section.defaults.output ?: FWD_P_DROP;
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 )
442 /* ... didn't work, fallback to option name */
443 fwd_read_zone_networks_cb(name, &net);
446 dtn->section.zone.networks = net;
447 dtn->type = FWD_S_ZONE;
448 dtn->next = cv->cursor;
453 static struct fwd_data *
454 fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
456 struct fwd_data_conveyor cv;
461 ucix_for_each_section_type(uci, "firewall", "zone",
462 (void *)fwd_read_zones_cb, &cv);
471 static void fwd_read_forwards_cb(
472 struct uci_context *uci,
473 const char *s, struct fwd_data_conveyor *cv
475 const char *src, *dest;
476 struct fwd_data *dtn;
477 struct fwd_zone *zsrc = NULL;
478 struct fwd_zone *zdest = NULL;
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);
489 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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);
496 dtn->type = FWD_S_FORWARD;
500 dtn->next = zsrc->forwardings;
501 zsrc->forwardings = dtn;
505 dtn->next = cv->cursor;
511 fwd_read_error("out of memory while parsing config!");
515 static struct fwd_data *
516 fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
518 struct fwd_data_conveyor cv;
523 ucix_for_each_section_type(uci, "firewall", "forwarding",
524 (void *)fwd_read_forwards_cb, &cv);
533 static void fwd_read_redirects_cb(
534 struct uci_context *uci,
535 const char *s, struct fwd_data_conveyor *cv
538 struct fwd_data *dtn = NULL;
539 struct fwd_data *dtn2 = NULL;
540 struct fwd_zone *zsrc = NULL;
543 if( !(src = fwd_read_string(uci, s, "src")) )
545 "section '%s' is missing 'src' option!",
549 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
551 "section '%s' references unknown src zone '%s'!",
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);
564 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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;
575 dtn->type = FWD_S_REDIRECT;
576 dtn->next = zsrc->redirects;
577 zsrc->redirects = dtn;
579 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
581 if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
582 !(dtn2->section.redirect.proto = fwd_alloc_ptr(struct fwd_proto))
585 fwd_read_error("out of memory while parsing config!");
588 dtn->section.redirect.proto->type = FWD_PR_UDP;
589 dtn2->section.redirect.proto->type = FWD_PR_TCP;
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;
600 dtn2->type = FWD_S_REDIRECT;
601 dtn2->next = zsrc->redirects;
602 zsrc->redirects = dtn2;
607 fwd_read_error("out of memory while parsing config!");
611 static struct fwd_data *
612 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
614 struct fwd_data_conveyor cv;
619 ucix_for_each_section_type(uci, "firewall", "redirect",
620 (void *)fwd_read_redirects_cb, &cv);
629 static void fwd_read_rules_cb(
630 struct uci_context *uci,
631 const char *s, struct fwd_data_conveyor *cv
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;
640 if( !(src = fwd_read_string(uci, s, "src")) )
642 "section '%s' is missing 'src' option!",
646 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
648 "section '%s' references unknown src zone '%s'!",
652 if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
653 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
655 "section '%s' references unknown dest zone '%s'!",
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);
668 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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");
681 dtn->type = FWD_S_RULE;
682 dtn->next = zsrc->rules;
685 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
687 if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
688 !(dtn2->section.rule.proto = fwd_alloc_ptr(struct fwd_proto))
691 fwd_read_error("out of memory while parsing config!");
694 dtn->section.rule.proto->type = FWD_PR_UDP;
695 dtn2->section.rule.proto->type = FWD_PR_TCP;
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;
707 dtn2->type = FWD_S_RULE;
708 dtn2->next = zsrc->rules;
714 fwd_read_error("out of memory while parsing config!");
718 static struct fwd_data *
719 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
721 struct fwd_data_conveyor cv;
726 ucix_for_each_section_type(uci, "firewall", "rule",
727 (void *)fwd_read_rules_cb, &cv);
736 static void fwd_read_includes_cb(
737 struct uci_context *uci,
738 const char *s, struct fwd_data_conveyor *cv
740 const char *path = fwd_read_string(uci, s, "path");
741 struct fwd_data *dtn = NULL;
745 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
747 dtn->section.include.path = strdup(path);
749 dtn->type = FWD_S_INCLUDE;
750 dtn->next = cv->cursor;
755 fwd_read_error("out of memory while parsing config!");
760 static struct fwd_data *
761 fwd_read_includes(struct uci_context *uci)
763 struct fwd_data_conveyor cv;
768 ucix_for_each_section_type(uci, "firewall", "include",
769 (void *)fwd_read_includes_cb, &cv);
778 static void fwd_read_network_data(
779 struct uci_context *uci, struct fwd_network *net
781 struct fwd_network *e;
782 const char *type, *ifname;
784 for( e = net; e; e = e->next )
786 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
788 if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
790 "section '%s' is missing 'ifname' option!",
794 e->isalias = (strcmp(type, "alias") ? 0 : 1);
795 e->ifname = strdup(ifname);
800 static void fwd_read_networks(
801 struct uci_context *uci, struct fwd_data *zones
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);
810 static void fwd_free_networks(struct fwd_network *h)
812 struct fwd_network *e = h;
818 fwd_free_ptr(h->name);
819 fwd_free_ptr(h->ifname);
820 fwd_free_ptr(h->addr);
829 static struct fwd_cidr * fwd_alloc_cidr(struct fwd_cidr *addr)
831 struct fwd_cidr *cidr;
833 if( (cidr = fwd_alloc_ptr(struct fwd_cidr)) != NULL )
837 cidr->addr.s_addr = addr->addr.s_addr;
838 cidr->prefix = addr->prefix;
849 struct fwd_data * fwd_read_config(struct fwd_handle *h)
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;
857 if( (ctx = ucix_init("firewall")) != NULL )
859 if( !(defaults = fwd_read_defaults(ctx)) )
862 if( !(zones = fwd_read_zones(ctx, defaults)) )
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));
873 if( (ctx = ucix_init("network")) != NULL )
875 fwd_read_networks(ctx, zones);
878 if( !(addrs = fwd_get_addrs(h->rtnl_socket, AF_INET)) )
881 for( e = zones; e && (zone = &e->section.zone); e = e->next )
883 if( e->type != FWD_S_ZONE )
886 for( net = zone->networks; net; net = net->next )
888 net->addr = fwd_alloc_cidr(
889 fwd_lookup_addr(addrs, net->ifname)
894 fwd_free_addrs(addrs);
900 if( ctx ) ucix_cleanup(ctx);
901 fwd_free_config(defaults);
902 fwd_free_config(zones);
907 void fwd_free_config(struct fwd_data *h)
909 struct fwd_data *e = h;
918 fwd_free_ptr(h->section.include.path);
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);
930 /* Clone rules share all pointers except proto.
931 Prevent a double-free here */
932 if( ! h->section.redirect.clone )
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);
941 fwd_free_ptr(h->section.redirect.proto);
945 /* Clone rules share all pointers except proto.
946 Prevent a double-free here */
947 if( ! h->section.rule.clone )
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);
956 fwd_free_ptr(h->section.rule.proto);
974 fwd_lookup_zone(struct fwd_data *h, const char *n)
980 for( e = h; e; e = e->next )
982 if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
983 return &e->section.zone;