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
;