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 fwd_log_err(__VA_ARGS__); \
38 fwd_read_policy(struct uci_context
*uci
, const char *s
, const char *o
)
40 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
64 fwd_read_bool(struct uci_context
*uci
, const char *s
, const char *o
, int d
)
66 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
70 if( !strcmp(val
, "yes") || !strcmp(val
, "true") || !strcmp(val
, "1") )
80 fwd_read_uint(struct uci_context
*uci
, const char *s
, const char *o
, unsigned int d
)
82 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
93 fwd_read_cidr(struct uci_context
*uci
, const char *s
, const char *o
, struct fwd_cidr
**c
)
95 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
96 char ip
[32], prefix
[32];
100 memset(prefix
, 0, 32);
106 else if( (strlen(val
) < 32) && (sscanf(val
, "%[^/]/%s", ip
, prefix
) > 0) )
108 if( !(*c
= fwd_alloc_ptr(struct fwd_cidr
)) )
111 if( inet_aton(ip
, &ina
) )
113 (*c
)->addr
.s_addr
= ina
.s_addr
;
115 if( strchr(prefix
, '.') )
117 if( inet_aton(prefix
, &ina
) )
120 ina
.s_addr
= ntohl(ina
.s_addr
);
122 while( !(ina
.s_addr
& 1) )
135 (*c
)->prefix
= prefix
[0] ? atoi(prefix
) : 32;
137 if( ((*c
)->prefix
< 0) || ((*c
)->prefix
> 32) )
153 fwd_read_mac(struct uci_context
*uci
, const char *s
, const char *o
, struct fwd_mac
**m
)
155 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
163 if( (*m
= fwd_alloc_ptr(struct fwd_mac
)) != NULL
)
165 unsigned int i1
, i2
, i3
, i4
, i5
, i6
;
167 if( sscanf(val
, "%2x:%2x:%2x:%2x:%2x:%2x",
168 &i1
, &i2
, &i3
, &i4
, &i5
, &i6
) == 6
170 (*m
)->mac
[0] = (unsigned char)i1
;
171 (*m
)->mac
[1] = (unsigned char)i2
;
172 (*m
)->mac
[2] = (unsigned char)i3
;
173 (*m
)->mac
[3] = (unsigned char)i4
;
174 (*m
)->mac
[4] = (unsigned char)i5
;
175 (*m
)->mac
[5] = (unsigned char)i6
;
186 fwd_read_portrange(struct uci_context
*uci
, const char *s
, const char *o
, struct fwd_portrange
**p
)
188 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
197 else if( sscanf(val
, "%u%*[:-]%u", &min
, &max
) > 0 )
210 if( (min
>= 0) && (min
<= 65535) && (max
>= 0) && (max
<= 65535) )
212 if( (*p
= fwd_alloc_ptr(struct fwd_portrange
)) != NULL
)
226 fwd_read_proto(struct uci_context
*uci
, const char *s
, const char *o
, struct fwd_proto
**p
)
228 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
237 if( (*p
= fwd_alloc_ptr(struct fwd_proto
)) != NULL
)
241 if( !strcasecmp(val
, "all") )
243 (*p
)->type
= FWD_PR_ALL
;
246 else if( !strcasecmp(val
, "icmp") )
248 (*p
)->type
= FWD_PR_ICMP
;
251 else if( !strcasecmp(val
, "udp") )
253 (*p
)->type
= FWD_PR_UDP
;
256 else if( !strcasecmp(val
, "tcp") )
258 (*p
)->type
= FWD_PR_TCP
;
261 else if( !strcasecmp(val
, "tcpudp") )
263 (*p
)->type
= FWD_PR_TCPUDP
;
268 (*p
)->type
= FWD_PR_CUSTOM
;
286 fwd_read_icmptype(struct uci_context
*uci
, const char *s
, const char *o
, struct fwd_icmptype
**i
)
288 const char *val
= ucix_get_option(uci
, "firewall", s
, o
);
289 unsigned int type
, code
;
297 if( (*i
= fwd_alloc_ptr(struct fwd_icmptype
)) != NULL
)
299 if( sscanf(val
, "%u/%u", &type
, &code
) == 2 )
301 if( (type
> 255) || (code
> 255) )
310 else if( sscanf(val
, "%u", &type
) == 1 )
321 /* XXX: no validity check here but I do not want to
322 duplicate libipt_icmp.c ... */
323 else if( sscanf(val
, "%31s", (*i
)->name
) == 1 )
336 fwd_read_string(struct uci_context
*uci
, const char *s
, const char *o
)
338 return ucix_get_option(uci
, "firewall", s
, o
);
343 fwd_append_config(struct fwd_data
*h
, struct fwd_data
*a
)
355 static void fwd_read_defaults_cb(
356 struct uci_context
*uci
,
357 const char *s
, struct fwd_defaults
*d
359 d
->input
= fwd_read_policy(uci
, s
, "input");
360 d
->forward
= fwd_read_policy(uci
, s
, "forward");
361 d
->output
= fwd_read_policy(uci
, s
, "output");
362 d
->syn_flood
= fwd_read_bool(uci
, s
, "syn_flood", 1);
363 d
->syn_rate
= fwd_read_uint(uci
, s
, "syn_rate", 25);
364 d
->syn_burst
= fwd_read_uint(uci
, s
, "syn_burst", 50);
365 d
->drop_invalid
= fwd_read_bool(uci
, s
, "drop_invalid", 1);
368 static struct fwd_data
*
369 fwd_read_defaults(struct uci_context
*uci
)
372 struct fwd_defaults d
;
374 if( (dt
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
376 memset(&d
, 0, sizeof(d
));
378 ucix_for_each_section_type(uci
, "firewall", "defaults",
379 (void *)fwd_read_defaults_cb
, &d
);
381 memcpy(&dt
->section
.defaults
, &d
, sizeof(d
));
383 dt
->type
= FWD_S_DEFAULTS
;
396 static void fwd_read_zone_networks_cb(
397 const char *net
, struct fwd_network
**np
399 struct fwd_network
*nn
;
401 if( (nn
= fwd_alloc_ptr(struct fwd_network
)) != NULL
)
403 nn
->name
= strdup(net
);
409 static void fwd_read_zones_cb(
410 struct uci_context
*uci
,
411 const char *s
, struct fwd_data_conveyor
*cv
413 struct fwd_data
*dtn
;
414 struct fwd_network
*net
= NULL
;
417 if( !(name
= fwd_read_string(uci
, s
, "name")) )
418 fwd_read_error("section '%s' is missing 'name' option!", s
);
420 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
422 dtn
->section
.zone
.name
= strdup(name
);
423 dtn
->section
.zone
.masq
= fwd_read_bool(uci
, s
, "masq", 0);
424 dtn
->section
.zone
.mtu_fix
= fwd_read_bool(uci
, s
, "mtu_fix", 0);
425 dtn
->section
.zone
.conntrack
= fwd_read_bool(uci
, s
, "conntrack", 0);
427 dtn
->section
.zone
.input
= fwd_read_policy(uci
, s
, "input")
428 ?: cv
->head
->section
.defaults
.input
?: FWD_P_DROP
;
430 dtn
->section
.zone
.forward
= fwd_read_policy(uci
, s
, "forward")
431 ?: cv
->head
->section
.defaults
.forward
?: FWD_P_DROP
;
433 dtn
->section
.zone
.output
= fwd_read_policy(uci
, s
, "output")
434 ?: cv
->head
->section
.defaults
.output
?: FWD_P_DROP
;
436 /* try to parse option/list network ... */
437 if( ucix_for_each_list(uci
, "firewall", s
, "network",
438 (void *)&fwd_read_zone_networks_cb
, &net
) < 0 )
440 /* ... didn't work, fallback to option name */
441 fwd_read_zone_networks_cb(name
, &net
);
444 dtn
->section
.zone
.networks
= net
;
445 dtn
->type
= FWD_S_ZONE
;
446 dtn
->next
= cv
->cursor
;
451 static struct fwd_data
*
452 fwd_read_zones(struct uci_context
*uci
, struct fwd_data
*def
)
454 struct fwd_data_conveyor cv
;
459 ucix_for_each_section_type(uci
, "firewall", "zone",
460 (void *)fwd_read_zones_cb
, &cv
);
469 static void fwd_read_forwards_cb(
470 struct uci_context
*uci
,
471 const char *s
, struct fwd_data_conveyor
*cv
473 const char *src
, *dest
;
474 struct fwd_data
*dtn
;
475 struct fwd_zone
*zsrc
= NULL
;
476 struct fwd_zone
*zdest
= NULL
;
478 if( !(src
= fwd_read_string(uci
, s
, "src")) )
479 fwd_read_error("section '%s' is missing 'src' option!", s
);
480 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
481 fwd_read_error("section '%s' references unknown src zone '%s'!", s
, src
);
482 else if( !(dest
= fwd_read_string(uci
, s
, "dest")) )
483 fwd_read_error("section '%s' is missing 'dest' option!", s
);
484 else if( !(zdest
= fwd_lookup_zone(cv
->head
, dest
)) )
485 fwd_read_error("section '%s' references unknown dest zone '%s'!", s
, dest
);
487 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
489 dtn
->section
.forwarding
.src
= zsrc
;
490 dtn
->section
.forwarding
.dest
= zdest
;
491 dtn
->section
.forwarding
.mtu_fix
= fwd_read_bool(uci
, s
, "mtu_fix", 0);
492 dtn
->section
.forwarding
.masq
= fwd_read_bool(uci
, s
, "masq", 0);
494 dtn
->type
= FWD_S_FORWARD
;
498 dtn
->next
= zsrc
->forwardings
;
499 zsrc
->forwardings
= dtn
;
503 dtn
->next
= cv
->cursor
;
509 fwd_read_error("out of memory while parsing config!");
513 static struct fwd_data
*
514 fwd_read_forwards(struct uci_context
*uci
, struct fwd_data
*zones
)
516 struct fwd_data_conveyor cv
;
521 ucix_for_each_section_type(uci
, "firewall", "forwarding",
522 (void *)fwd_read_forwards_cb
, &cv
);
531 static void fwd_read_redirects_cb(
532 struct uci_context
*uci
,
533 const char *s
, struct fwd_data_conveyor
*cv
536 struct fwd_data
*dtn
= NULL
;
537 struct fwd_data
*dtn2
= NULL
;
538 struct fwd_zone
*zsrc
= NULL
;
541 if( !(src
= fwd_read_string(uci
, s
, "src")) )
543 "section '%s' is missing 'src' option!",
547 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
549 "section '%s' references unknown src zone '%s'!",
553 /* uci context, section, name, type */
554 fwd_check_option(uci
, s
, src_ip
, cidr
);
555 fwd_check_option(uci
, s
, src_mac
, mac
);
556 fwd_check_option(uci
, s
, src_port
, portrange
);
557 fwd_check_option(uci
, s
, src_dport
, portrange
);
558 fwd_check_option(uci
, s
, dest_ip
, cidr
);
559 fwd_check_option(uci
, s
, dest_port
, portrange
);
560 fwd_check_option(uci
, s
, proto
, proto
);
562 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
564 dtn
->section
.redirect
.proto
= proto
;
565 dtn
->section
.redirect
.src
= zsrc
;
566 dtn
->section
.redirect
.src_ip
= src_ip
;
567 dtn
->section
.redirect
.src_mac
= src_mac
;
568 dtn
->section
.redirect
.src_port
= src_port
;
569 dtn
->section
.redirect
.src_dport
= src_dport
;
570 dtn
->section
.redirect
.dest_ip
= dest_ip
;
571 dtn
->section
.redirect
.dest_port
= dest_port
;
573 dtn
->type
= FWD_S_REDIRECT
;
574 dtn
->next
= zsrc
->redirects
;
575 zsrc
->redirects
= dtn
;
577 if( (proto
!= NULL
) && (proto
->type
== FWD_PR_TCPUDP
) )
579 if( !(dtn2
= fwd_alloc_ptr(struct fwd_data
)) ||
580 !(dtn2
->section
.redirect
.proto
= fwd_alloc_ptr(struct fwd_proto
))
583 fwd_read_error("out of memory while parsing config!");
586 dtn
->section
.redirect
.proto
->type
= FWD_PR_UDP
;
587 dtn2
->section
.redirect
.proto
->type
= FWD_PR_TCP
;
589 dtn2
->section
.redirect
.src
= zsrc
;
590 dtn2
->section
.redirect
.src_ip
= src_ip
;
591 dtn2
->section
.redirect
.src_mac
= src_mac
;
592 dtn2
->section
.redirect
.src_port
= src_port
;
593 dtn2
->section
.redirect
.src_dport
= src_dport
;
594 dtn2
->section
.redirect
.dest_ip
= dest_ip
;
595 dtn2
->section
.redirect
.dest_port
= dest_port
;
596 dtn2
->section
.redirect
.clone
= 1;
598 dtn2
->type
= FWD_S_REDIRECT
;
599 dtn2
->next
= zsrc
->redirects
;
600 zsrc
->redirects
= dtn2
;
605 fwd_read_error("out of memory while parsing config!");
609 static struct fwd_data
*
610 fwd_read_redirects(struct uci_context
*uci
, struct fwd_data
*zones
)
612 struct fwd_data_conveyor cv
;
617 ucix_for_each_section_type(uci
, "firewall", "redirect",
618 (void *)fwd_read_redirects_cb
, &cv
);
627 static void fwd_read_rules_cb(
628 struct uci_context
*uci
,
629 const char *s
, struct fwd_data_conveyor
*cv
631 const char *src
, *dest
;
632 struct fwd_data
*dtn
= NULL
;
633 struct fwd_data
*dtn2
= NULL
;
634 struct fwd_zone
*zsrc
= NULL
;
635 struct fwd_zone
*zdest
= NULL
;
638 if( !(src
= fwd_read_string(uci
, s
, "src")) )
640 "section '%s' is missing 'src' option!",
644 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
646 "section '%s' references unknown src zone '%s'!",
650 if( (dest
= fwd_read_string(uci
, s
, "dest")) != NULL
)
651 if( !(zdest
= fwd_lookup_zone(cv
->head
, dest
)) )
653 "section '%s' references unknown dest zone '%s'!",
657 /* uci context, section, name, type */
658 fwd_check_option(uci
, s
, src_ip
, cidr
);
659 fwd_check_option(uci
, s
, src_mac
, mac
);
660 fwd_check_option(uci
, s
, src_port
, portrange
);
661 fwd_check_option(uci
, s
, dest_ip
, cidr
);
662 fwd_check_option(uci
, s
, dest_port
, portrange
);
663 fwd_check_option(uci
, s
, proto
, proto
);
664 fwd_check_option(uci
, s
, icmptype
, icmptype
);
666 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
668 dtn
->section
.rule
.proto
= proto
;
669 dtn
->section
.rule
.icmp_type
= icmptype
;
670 dtn
->section
.rule
.src
= zsrc
;
671 dtn
->section
.rule
.src_ip
= src_ip
;
672 dtn
->section
.rule
.src_mac
= src_mac
;
673 dtn
->section
.rule
.src_port
= src_port
;
674 dtn
->section
.rule
.dest
= zdest
;
675 dtn
->section
.rule
.dest_ip
= dest_ip
;
676 dtn
->section
.rule
.dest_port
= dest_port
;
677 dtn
->section
.rule
.target
= fwd_read_policy(uci
, s
, "target");
679 dtn
->type
= FWD_S_RULE
;
680 dtn
->next
= zsrc
->rules
;
683 if( (proto
!= NULL
) && (proto
->type
== FWD_PR_TCPUDP
) )
685 if( !(dtn2
= fwd_alloc_ptr(struct fwd_data
)) ||
686 !(dtn2
->section
.rule
.proto
= fwd_alloc_ptr(struct fwd_proto
))
689 fwd_read_error("out of memory while parsing config!");
692 dtn
->section
.rule
.proto
->type
= FWD_PR_UDP
;
693 dtn2
->section
.rule
.proto
->type
= FWD_PR_TCP
;
695 dtn2
->section
.rule
.src
= zsrc
;
696 dtn2
->section
.rule
.src_ip
= src_ip
;
697 dtn2
->section
.rule
.src_mac
= src_mac
;
698 dtn2
->section
.rule
.src_port
= src_port
;
699 dtn2
->section
.rule
.dest
= zdest
;
700 dtn2
->section
.rule
.dest_ip
= dest_ip
;
701 dtn2
->section
.rule
.dest_port
= dest_port
;
702 dtn2
->section
.rule
.target
= dtn
->section
.rule
.target
;
703 dtn2
->section
.rule
.clone
= 1;
705 dtn2
->type
= FWD_S_RULE
;
706 dtn2
->next
= zsrc
->rules
;
712 fwd_read_error("out of memory while parsing config!");
716 static struct fwd_data
*
717 fwd_read_rules(struct uci_context
*uci
, struct fwd_data
*zones
)
719 struct fwd_data_conveyor cv
;
724 ucix_for_each_section_type(uci
, "firewall", "rule",
725 (void *)fwd_read_rules_cb
, &cv
);
734 static void fwd_read_includes_cb(
735 struct uci_context
*uci
,
736 const char *s
, struct fwd_data_conveyor
*cv
738 const char *path
= fwd_read_string(uci
, s
, "path");
739 struct fwd_data
*dtn
= NULL
;
743 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
745 dtn
->section
.include
.path
= strdup(path
);
747 dtn
->type
= FWD_S_INCLUDE
;
748 dtn
->next
= cv
->cursor
;
753 fwd_read_error("out of memory while parsing config!");
758 static struct fwd_data
*
759 fwd_read_includes(struct uci_context
*uci
)
761 struct fwd_data_conveyor cv
;
766 ucix_for_each_section_type(uci
, "firewall", "include",
767 (void *)fwd_read_includes_cb
, &cv
);
776 static void fwd_read_network_data(
777 struct uci_context
*uci
, struct fwd_network
*net
779 struct fwd_network
*e
;
780 const char *type
, *ifname
;
782 for( e
= net
; e
; e
= e
->next
)
784 if( (type
= ucix_get_option(uci
, "network", e
->name
, NULL
)) != NULL
)
786 if( !(ifname
= ucix_get_option(uci
, "network", e
->name
, "ifname")) )
788 "section '%s' is missing 'ifname' option!",
792 e
->isalias
= (strcmp(type
, "alias") ? 0 : 1);
793 e
->ifname
= strdup(ifname
);
798 static void fwd_read_networks(
799 struct uci_context
*uci
, struct fwd_data
*zones
803 for( e
= zones
; e
; e
= e
->next
)
804 if( e
->type
== FWD_S_ZONE
)
805 fwd_read_network_data(uci
, e
->section
.zone
.networks
);
808 static void fwd_free_networks(struct fwd_network
*h
)
810 struct fwd_network
*e
= h
;
816 fwd_free_ptr(h
->name
);
817 fwd_free_ptr(h
->ifname
);
818 fwd_free_ptr(h
->addr
);
827 static struct fwd_cidr
* fwd_alloc_cidr(struct fwd_cidr
*addr
)
829 struct fwd_cidr
*cidr
;
831 if( (cidr
= fwd_alloc_ptr(struct fwd_cidr
)) != NULL
)
835 cidr
->addr
.s_addr
= addr
->addr
.s_addr
;
836 cidr
->prefix
= addr
->prefix
;
847 struct fwd_data
* fwd_read_config(struct fwd_handle
*h
)
849 struct uci_context
*ctx
;
850 struct fwd_data
*defaults
, *zones
, *e
;
851 struct fwd_addr
*addrs
;
852 struct fwd_network
*net
;
853 struct fwd_zone
*zone
;
855 if( (ctx
= ucix_init("firewall")) != NULL
)
857 if( !(defaults
= fwd_read_defaults(ctx
)) )
860 if( !(zones
= fwd_read_zones(ctx
, defaults
)) )
863 fwd_append_config(defaults
, zones
);
864 fwd_append_config(defaults
, fwd_read_forwards(ctx
, zones
));
865 fwd_append_config(defaults
, fwd_read_redirects(ctx
, zones
));
866 fwd_append_config(defaults
, fwd_read_rules(ctx
, zones
));
867 fwd_append_config(defaults
, fwd_read_includes(ctx
));
871 if( (ctx
= ucix_init("network")) != NULL
)
873 fwd_read_networks(ctx
, zones
);
876 if( !(addrs
= fwd_get_addrs(h
->rtnl_socket
, AF_INET
)) )
879 for( e
= zones
; e
&& (zone
= &e
->section
.zone
); e
= e
->next
)
881 if( e
->type
!= FWD_S_ZONE
)
884 for( net
= zone
->networks
; net
; net
= net
->next
)
886 net
->addr
= fwd_alloc_cidr(
887 fwd_lookup_addr(addrs
, net
->ifname
)
892 fwd_free_addrs(addrs
);
898 if( ctx
) ucix_cleanup(ctx
);
899 fwd_free_config(defaults
);
900 fwd_free_config(zones
);
905 void fwd_free_config(struct fwd_data
*h
)
907 struct fwd_data
*e
= h
;
916 fwd_free_ptr(h
->section
.include
.path
);
920 fwd_free_ptr(h
->section
.zone
.name
);
921 fwd_free_networks(h
->section
.zone
.networks
);
922 fwd_free_config(h
->section
.zone
.rules
);
923 fwd_free_config(h
->section
.zone
.redirects
);
924 fwd_free_config(h
->section
.zone
.forwardings
);
928 /* Clone rules share all pointers except proto.
929 Prevent a double-free here */
930 if( ! h
->section
.redirect
.clone
)
932 fwd_free_ptr(h
->section
.redirect
.src_ip
);
933 fwd_free_ptr(h
->section
.redirect
.src_mac
);
934 fwd_free_ptr(h
->section
.redirect
.src_port
);
935 fwd_free_ptr(h
->section
.redirect
.src_dport
);
936 fwd_free_ptr(h
->section
.redirect
.dest_ip
);
937 fwd_free_ptr(h
->section
.redirect
.dest_port
);
939 fwd_free_ptr(h
->section
.redirect
.proto
);
943 /* Clone rules share all pointers except proto.
944 Prevent a double-free here */
945 if( ! h
->section
.rule
.clone
)
947 fwd_free_ptr(h
->section
.rule
.src_ip
);
948 fwd_free_ptr(h
->section
.rule
.src_mac
);
949 fwd_free_ptr(h
->section
.rule
.src_port
);
950 fwd_free_ptr(h
->section
.rule
.dest_ip
);
951 fwd_free_ptr(h
->section
.rule
.dest_port
);
952 fwd_free_ptr(h
->section
.rule
.icmp_type
);
954 fwd_free_ptr(h
->section
.rule
.proto
);
972 fwd_lookup_zone(struct fwd_data
*h
, const char *n
)
978 for( e
= h
; e
; e
= e
->next
)
980 if( (e
->type
= FWD_S_ZONE
) && !strcmp(e
->section
.zone
.name
, n
) )
981 return &e
->section
.zone
;