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 unsigned int i1, i2, i3, i4, i5, i6;
168 if( sscanf(val, "%2x:%2x:%2x:%2x:%2x:%2x",
169 &i1, &i2, &i3, &i4, &i5, &i6) == 6
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;
187 fwd_read_portrange(struct uci_context *uci, const char *s, const char *o, struct fwd_portrange **p)
189 const char *val = ucix_get_option(uci, "firewall", s, o);
198 else if( sscanf(val, "%u%*[:-]%u", &min, &max) > 0 )
211 if( (min >= 0) && (min <= 65535) && (max >= 0) && (max <= 65535) )
213 if( (*p = fwd_alloc_ptr(struct fwd_portrange)) != NULL )
227 fwd_read_proto(struct uci_context *uci, const char *s, const char *o, struct fwd_proto **p)
229 const char *val = ucix_get_option(uci, "firewall", s, o);
238 if( (*p = fwd_alloc_ptr(struct fwd_proto)) != NULL )
242 if( !strcasecmp(val, "all") )
244 (*p)->type = FWD_PR_ALL;
247 else if( !strcasecmp(val, "icmp") )
249 (*p)->type = FWD_PR_ICMP;
252 else if( !strcasecmp(val, "udp") )
254 (*p)->type = FWD_PR_UDP;
257 else if( !strcasecmp(val, "tcp") )
259 (*p)->type = FWD_PR_TCP;
262 else if( !strcasecmp(val, "tcpudp") )
264 (*p)->type = FWD_PR_TCPUDP;
269 (*p)->type = FWD_PR_CUSTOM;
287 fwd_read_icmptype(struct uci_context *uci, const char *s, const char *o, struct fwd_icmptype **i)
289 const char *val = ucix_get_option(uci, "firewall", s, o);
290 unsigned int type, code;
298 if( (*i = fwd_alloc_ptr(struct fwd_icmptype)) != NULL )
300 if( sscanf(val, "%u/%u", &type, &code) == 2 )
302 if( (type > 255) || (code > 255) )
311 else if( sscanf(val, "%u", &type) == 1 )
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 )
337 fwd_read_string(struct uci_context *uci, const char *s, const char *o)
339 return ucix_get_option(uci, "firewall", s, o);
344 fwd_append_config(struct fwd_data *h, struct fwd_data *a)
356 static void fwd_read_defaults_cb(
357 struct uci_context *uci,
358 const char *s, struct fwd_defaults *d
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);
369 static struct fwd_data *
370 fwd_read_defaults(struct uci_context *uci)
373 struct fwd_defaults d;
375 if( (dt = fwd_alloc_ptr(struct fwd_data)) != NULL )
377 memset(&d, 0, sizeof(d));
379 ucix_for_each_section_type(uci, "firewall", "defaults",
380 (void *)fwd_read_defaults_cb, &d);
382 memcpy(&dt->section.defaults, &d, sizeof(d));
384 dt->type = FWD_S_DEFAULTS;
397 static void fwd_read_zone_networks_cb(
398 const char *net, struct fwd_network_list **np
400 struct fwd_network_list *nn;
402 if( (nn = fwd_alloc_ptr(struct fwd_network_list)) != NULL )
404 nn->name = strdup(net);
410 static void fwd_read_zones_cb(
411 struct uci_context *uci,
412 const char *s, struct fwd_data_conveyor *cv
414 struct fwd_data *dtn;
415 struct fwd_network_list *net = NULL;
418 if( !(name = fwd_read_string(uci, s, "name")) )
419 fwd_read_error("section '%s' is missing 'name' option!", s);
421 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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);
428 dtn->section.zone.input = fwd_read_policy(uci, s, "input")
429 ?: cv->head->section.defaults.input ?: FWD_P_DROP;
431 dtn->section.zone.forward = fwd_read_policy(uci, s, "forward")
432 ?: cv->head->section.defaults.forward ?: FWD_P_DROP;
434 dtn->section.zone.output = fwd_read_policy(uci, s, "output")
435 ?: cv->head->section.defaults.output ?: FWD_P_DROP;
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 )
441 /* ... didn't work, fallback to option name */
442 fwd_read_zone_networks_cb(name, &net);
445 dtn->section.zone.networks = net;
446 dtn->type = FWD_S_ZONE;
447 dtn->next = cv->cursor;
452 static struct fwd_data *
453 fwd_read_zones(struct uci_context *uci, struct fwd_data *def)
455 struct fwd_data_conveyor cv;
460 ucix_for_each_section_type(uci, "firewall", "zone",
461 (void *)fwd_read_zones_cb, &cv);
470 static void fwd_read_forwards_cb(
471 struct uci_context *uci,
472 const char *s, struct fwd_data_conveyor *cv
474 const char *src, *dest;
475 struct fwd_data *dtn;
476 struct fwd_zone *zsrc = NULL;
477 struct fwd_zone *zdest = NULL;
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);
488 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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);
495 dtn->type = FWD_S_FORWARD;
499 dtn->next = zsrc->forwardings;
500 zsrc->forwardings = dtn;
504 dtn->next = cv->cursor;
510 fwd_read_error("out of memory while parsing config!");
514 static struct fwd_data *
515 fwd_read_forwards(struct uci_context *uci, struct fwd_data *zones)
517 struct fwd_data_conveyor cv;
522 ucix_for_each_section_type(uci, "firewall", "forwarding",
523 (void *)fwd_read_forwards_cb, &cv);
532 static void fwd_read_redirects_cb(
533 struct uci_context *uci,
534 const char *s, struct fwd_data_conveyor *cv
537 struct fwd_data *dtn = NULL;
538 struct fwd_data *dtn2 = NULL;
539 struct fwd_zone *zsrc = NULL;
542 if( !(src = fwd_read_string(uci, s, "src")) )
544 "section '%s' is missing 'src' option!",
548 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
550 "section '%s' references unknown src zone '%s'!",
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);
563 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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;
574 dtn->type = FWD_S_REDIRECT;
575 dtn->next = zsrc->redirects;
576 zsrc->redirects = dtn;
578 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
580 if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
581 !(dtn2->section.redirect.proto = fwd_alloc_ptr(struct fwd_proto))
584 fwd_read_error("out of memory while parsing config!");
587 dtn->section.redirect.proto->type = FWD_PR_UDP;
588 dtn2->section.redirect.proto->type = FWD_PR_TCP;
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;
599 dtn2->type = FWD_S_REDIRECT;
600 dtn2->next = zsrc->redirects;
601 zsrc->redirects = dtn2;
606 fwd_read_error("out of memory while parsing config!");
610 static struct fwd_data *
611 fwd_read_redirects(struct uci_context *uci, struct fwd_data *zones)
613 struct fwd_data_conveyor cv;
618 ucix_for_each_section_type(uci, "firewall", "redirect",
619 (void *)fwd_read_redirects_cb, &cv);
628 static void fwd_read_rules_cb(
629 struct uci_context *uci,
630 const char *s, struct fwd_data_conveyor *cv
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;
639 if( !(src = fwd_read_string(uci, s, "src")) )
641 "section '%s' is missing 'src' option!",
645 else if( !(zsrc = fwd_lookup_zone(cv->head, src)) )
647 "section '%s' references unknown src zone '%s'!",
651 if( (dest = fwd_read_string(uci, s, "dest")) != NULL )
652 if( !(zdest = fwd_lookup_zone(cv->head, dest)) )
654 "section '%s' references unknown dest zone '%s'!",
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);
667 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
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");
680 dtn->type = FWD_S_RULE;
681 dtn->next = zsrc->rules;
684 if( (proto != NULL) && (proto->type == FWD_PR_TCPUDP) )
686 if( !(dtn2 = fwd_alloc_ptr(struct fwd_data)) ||
687 !(dtn2->section.rule.proto = fwd_alloc_ptr(struct fwd_proto))
690 fwd_read_error("out of memory while parsing config!");
693 dtn->section.rule.proto->type = FWD_PR_UDP;
694 dtn2->section.rule.proto->type = FWD_PR_TCP;
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;
706 dtn2->type = FWD_S_RULE;
707 dtn2->next = zsrc->rules;
713 fwd_read_error("out of memory while parsing config!");
717 static struct fwd_data *
718 fwd_read_rules(struct uci_context *uci, struct fwd_data *zones)
720 struct fwd_data_conveyor cv;
725 ucix_for_each_section_type(uci, "firewall", "rule",
726 (void *)fwd_read_rules_cb, &cv);
735 static void fwd_read_includes_cb(
736 struct uci_context *uci,
737 const char *s, struct fwd_data_conveyor *cv
739 const char *path = fwd_read_string(uci, s, "path");
740 struct fwd_data *dtn = NULL;
744 if( (dtn = fwd_alloc_ptr(struct fwd_data)) != NULL )
746 dtn->section.include.path = strdup(path);
748 dtn->type = FWD_S_INCLUDE;
749 dtn->next = cv->cursor;
754 fwd_read_error("out of memory while parsing config!");
759 static struct fwd_data *
760 fwd_read_includes(struct uci_context *uci)
762 struct fwd_data_conveyor cv;
767 ucix_for_each_section_type(uci, "firewall", "include",
768 (void *)fwd_read_includes_cb, &cv);
777 static void fwd_read_network_data(
778 struct uci_context *uci, struct fwd_network_list *net
780 struct fwd_network_list *e;
781 const char *type, *ifname;
783 for( e = net; e; e = e->next )
785 if( (type = ucix_get_option(uci, "network", e->name, NULL)) != NULL )
787 if( !(ifname = ucix_get_option(uci, "network", e->name, "ifname")) )
789 "section '%s' is missing 'ifname' option!",
793 e->isalias = (strcmp(type, "alias") ? 0 : 1);
794 e->ifname = strdup(ifname);
799 static void fwd_read_networks(
800 struct uci_context *uci, struct fwd_data *zones
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);
809 static void fwd_free_networks(struct fwd_network_list *h)
811 struct fwd_network_list *e = h;
817 fwd_free_ptr(h->name);
818 fwd_free_ptr(h->ifname);
819 fwd_free_ptr(h->addr);
830 struct fwd_data * fwd_read_config(void)
832 struct uci_context *ctx;
833 struct fwd_data *defaults, *zones;
835 if( (ctx = ucix_init("firewall")) != NULL )
837 if( !(defaults = fwd_read_defaults(ctx)) )
840 if( !(zones = fwd_read_zones(ctx, defaults)) )
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));
851 if( (ctx = ucix_init("network")) != NULL )
853 fwd_read_networks(ctx, zones);
861 if( ctx ) ucix_cleanup(ctx);
862 fwd_free_config(defaults);
863 fwd_free_config(zones);
868 void fwd_free_config(struct fwd_data *h)
870 struct fwd_data *e = h;
879 fwd_free_ptr(h->section.include.path);
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);
891 /* Clone rules share all pointers except proto.
892 Prevent a double-free here */
893 if( ! h->section.redirect.clone )
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);
902 fwd_free_ptr(h->section.redirect.proto);
906 /* Clone rules share all pointers except proto.
907 Prevent a double-free here */
908 if( ! h->section.rule.clone )
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);
917 fwd_free_ptr(h->section.rule.proto);
935 fwd_lookup_zone(struct fwd_data *h, const char *n)
941 for( e = h; e; e = e->next )
943 if( (e->type = FWD_S_ZONE) && !strcmp(e->section.zone.name, n) )
944 return &e->section.zone;