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_data
*dtn2
= NULL
;
527 struct fwd_zone
*zsrc
= NULL
;
530 if( !(src
= fwd_read_string(uci
, s
, "src")) )
532 "section '%s' is missing 'src' option!",
536 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
538 "section '%s' references unknown src zone '%s'!",
542 /* uci context, section, name, type */
543 fwd_check_option(uci
, s
, src_ip
, cidr
);
544 fwd_check_option(uci
, s
, src_mac
, mac
);
545 fwd_check_option(uci
, s
, src_port
, portrange
);
546 fwd_check_option(uci
, s
, src_dport
, portrange
);
547 fwd_check_option(uci
, s
, dest_ip
, cidr
);
548 fwd_check_option(uci
, s
, dest_port
, portrange
);
549 fwd_check_option(uci
, s
, proto
, proto
);
551 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
553 dtn
->section
.redirect
.proto
= proto
;
554 dtn
->section
.redirect
.src
= zsrc
;
555 dtn
->section
.redirect
.src_ip
= src_ip
;
556 dtn
->section
.redirect
.src_mac
= src_mac
;
557 dtn
->section
.redirect
.src_port
= src_port
;
558 dtn
->section
.redirect
.src_dport
= src_dport
;
559 dtn
->section
.redirect
.dest_ip
= dest_ip
;
560 dtn
->section
.redirect
.dest_port
= dest_port
;
562 dtn
->type
= FWD_S_REDIRECT
;
563 dtn
->next
= cv
->cursor
;
566 if( (proto
!= NULL
) && (proto
->type
== FWD_PR_TCPUDP
) )
568 if( !(dtn2
= fwd_alloc_ptr(struct fwd_data
)) ||
569 !(dtn2
->section
.redirect
.proto
= fwd_alloc_ptr(struct fwd_proto
))
572 fwd_read_error("out of memory while parsing config!");
575 dtn
->section
.redirect
.proto
->type
= FWD_PR_UDP
;
576 dtn2
->section
.redirect
.proto
->type
= FWD_PR_TCP
;
578 dtn2
->section
.redirect
.src
= zsrc
;
579 dtn2
->section
.redirect
.src_ip
= src_ip
;
580 dtn2
->section
.redirect
.src_mac
= src_mac
;
581 dtn2
->section
.redirect
.src_port
= src_port
;
582 dtn2
->section
.redirect
.src_dport
= src_dport
;
583 dtn2
->section
.redirect
.dest_ip
= dest_ip
;
584 dtn2
->section
.redirect
.dest_port
= dest_port
;
586 dtn2
->type
= FWD_S_REDIRECT
;
587 dtn2
->next
= cv
->cursor
;
593 fwd_read_error("out of memory while parsing config!");
597 static struct fwd_data
*
598 fwd_read_redirects(struct uci_context
*uci
, struct fwd_data
*zones
)
600 struct fwd_data_conveyor cv
;
605 ucix_for_each_section_type(uci
, "firewall", "redirect",
606 (void *)fwd_read_redirects_cb
, &cv
);
615 static void fwd_read_rules_cb(
616 struct uci_context
*uci
,
617 const char *s
, struct fwd_data_conveyor
*cv
619 const char *src
, *dest
;
620 struct fwd_data
*dtn
= NULL
;
621 struct fwd_data
*dtn2
= NULL
;
622 struct fwd_zone
*zsrc
= NULL
;
623 struct fwd_zone
*zdest
= NULL
;
626 if( !(src
= fwd_read_string(uci
, s
, "src")) )
628 "section '%s' is missing 'src' option!",
632 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
634 "section '%s' references unknown src zone '%s'!",
638 if( (dest
= fwd_read_string(uci
, s
, "dest")) != NULL
)
639 if( !(zdest
= fwd_lookup_zone(cv
->head
, dest
)) )
641 "section '%s' references unknown dest zone '%s'!",
645 /* uci context, section, name, type */
646 fwd_check_option(uci
, s
, src_ip
, cidr
);
647 fwd_check_option(uci
, s
, src_mac
, mac
);
648 fwd_check_option(uci
, s
, src_port
, portrange
);
649 fwd_check_option(uci
, s
, dest_ip
, cidr
);
650 fwd_check_option(uci
, s
, dest_port
, portrange
);
651 fwd_check_option(uci
, s
, proto
, proto
);
652 fwd_check_option(uci
, s
, icmptype
, icmptype
);
654 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
656 dtn
->section
.rule
.proto
= proto
;
657 dtn
->section
.rule
.icmp_type
= icmptype
;
658 dtn
->section
.rule
.src
= zsrc
;
659 dtn
->section
.rule
.src_ip
= src_ip
;
660 dtn
->section
.rule
.src_mac
= src_mac
;
661 dtn
->section
.rule
.src_port
= src_port
;
662 dtn
->section
.rule
.dest
= zdest
;
663 dtn
->section
.rule
.dest_ip
= dest_ip
;
664 dtn
->section
.rule
.dest_port
= dest_port
;
665 dtn
->section
.rule
.target
= fwd_read_policy(uci
, s
, "target");
667 dtn
->type
= FWD_S_RULE
;
668 dtn
->next
= cv
->cursor
;
671 if( (proto
!= NULL
) && (proto
->type
== FWD_PR_TCPUDP
) )
673 if( !(dtn2
= fwd_alloc_ptr(struct fwd_data
)) ||
674 !(dtn2
->section
.rule
.proto
= fwd_alloc_ptr(struct fwd_proto
))
677 fwd_read_error("out of memory while parsing config!");
680 dtn
->section
.rule
.proto
->type
= FWD_PR_UDP
;
681 dtn2
->section
.rule
.proto
->type
= FWD_PR_TCP
;
683 dtn2
->section
.rule
.src
= zsrc
;
684 dtn2
->section
.rule
.src_ip
= src_ip
;
685 dtn2
->section
.rule
.src_mac
= src_mac
;
686 dtn2
->section
.rule
.src_port
= src_port
;
687 dtn2
->section
.rule
.dest
= zdest
;
688 dtn2
->section
.rule
.dest_ip
= dest_ip
;
689 dtn2
->section
.rule
.dest_port
= dest_port
;
690 dtn2
->section
.rule
.target
= dtn
->section
.rule
.target
;
692 dtn2
->type
= FWD_S_RULE
;
693 dtn2
->next
= cv
->cursor
;
699 fwd_read_error("out of memory while parsing config!");
703 static struct fwd_data
*
704 fwd_read_rules(struct uci_context
*uci
, struct fwd_data
*zones
)
706 struct fwd_data_conveyor cv
;
711 ucix_for_each_section_type(uci
, "firewall", "rule",
712 (void *)fwd_read_rules_cb
, &cv
);
721 static void fwd_read_includes_cb(
722 struct uci_context
*uci
,
723 const char *s
, struct fwd_data_conveyor
*cv
725 const char *path
= fwd_read_string(uci
, s
, "path");
726 struct fwd_data
*dtn
= NULL
;
730 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
732 dtn
->section
.include
.path
= strdup(path
);
734 dtn
->type
= FWD_S_INCLUDE
;
735 dtn
->next
= cv
->cursor
;
740 fwd_read_error("out of memory while parsing config!");
745 static struct fwd_data
*
746 fwd_read_includes(struct uci_context
*uci
)
748 struct fwd_data_conveyor cv
;
753 ucix_for_each_section_type(uci
, "firewall", "include",
754 (void *)fwd_read_includes_cb
, &cv
);
763 static void fwd_read_network_data(
764 struct uci_context
*uci
, struct fwd_network_list
*net
766 struct fwd_network_list
*e
;
767 const char *type
, *ifname
;
769 for( e
= net
; e
; e
= e
->next
)
771 if( (type
= ucix_get_option(uci
, "network", e
->name
, NULL
)) != NULL
)
773 if( !(ifname
= ucix_get_option(uci
, "network", e
->name
, "ifname")) )
775 "section '%s' is missing 'ifname' option!",
779 e
->isalias
= (strcmp(type
, "alias") ? 0 : 1);
780 e
->ifname
= strdup(ifname
);
785 static void fwd_read_networks(
786 struct uci_context
*uci
, struct fwd_data
*zones
790 for( e
= zones
; e
; e
= e
->next
)
791 if( e
->type
== FWD_S_ZONE
)
792 fwd_read_network_data(uci
, e
->section
.zone
.networks
);
795 static void fwd_free_networks(struct fwd_network_list
*h
)
797 struct fwd_network_list
*e
= h
;
803 fwd_free_ptr(h
->name
);
804 fwd_free_ptr(h
->ifname
);
805 fwd_free_ptr(h
->addr
);
816 struct fwd_data
* fwd_read_config(void)
818 struct uci_context
*ctx
;
819 struct fwd_data
*defaults
, *zones
;
821 if( (ctx
= ucix_init("firewall")) != NULL
)
823 if( !(defaults
= fwd_read_defaults(ctx
)) )
826 if( !(zones
= fwd_read_zones(ctx
, defaults
)) )
829 fwd_append_config(defaults
, zones
);
830 fwd_append_config(defaults
, fwd_read_forwards(ctx
, zones
));
831 fwd_append_config(defaults
, fwd_read_redirects(ctx
, zones
));
832 fwd_append_config(defaults
, fwd_read_rules(ctx
, zones
));
833 fwd_append_config(defaults
, fwd_read_includes(ctx
));
837 if( (ctx
= ucix_init("network")) != NULL
)
839 fwd_read_networks(ctx
, zones
);
847 if( ctx
) ucix_cleanup(ctx
);
848 fwd_free_config(defaults
);
849 fwd_free_config(zones
);
854 void fwd_free_config(struct fwd_data
*h
)
856 struct fwd_data
*e
= h
;
865 fwd_free_ptr(h
->section
.include
.path
);
869 fwd_free_ptr(h
->section
.zone
.name
);
870 fwd_free_networks(h
->section
.zone
.networks
);
874 fwd_free_ptr(h
->section
.redirect
.src_ip
);
875 fwd_free_ptr(h
->section
.redirect
.src_mac
);
876 fwd_free_ptr(h
->section
.redirect
.src_port
);
877 fwd_free_ptr(h
->section
.redirect
.src_dport
);
878 fwd_free_ptr(h
->section
.redirect
.dest_ip
);
879 fwd_free_ptr(h
->section
.redirect
.dest_port
);
880 fwd_free_ptr(h
->section
.redirect
.proto
);
884 fwd_free_ptr(h
->section
.rule
.src_ip
);
885 fwd_free_ptr(h
->section
.rule
.src_mac
);
886 fwd_free_ptr(h
->section
.rule
.src_port
);
887 fwd_free_ptr(h
->section
.rule
.dest_ip
);
888 fwd_free_ptr(h
->section
.rule
.dest_port
);
889 fwd_free_ptr(h
->section
.rule
.proto
);
890 fwd_free_ptr(h
->section
.rule
.icmp_type
);
908 fwd_lookup_zone(struct fwd_data
*h
, const char *n
)
914 for( e
= h
; e
; e
= e
->next
)
916 if( (e
->type
= FWD_S_ZONE
) && !strcmp(e
->section
.zone
.name
, n
) )
917 return &e
->section
.zone
;