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_zone
*zsrc
= NULL
;
529 if( !(src
= fwd_read_string(uci
, s
, "src")) )
531 "section '%s' is missing 'src' option!",
535 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
537 "section '%s' references unknown src zone '%s'!",
541 /* uci context, section, name, type */
542 fwd_check_option(uci
, s
, src_ip
, cidr
);
543 fwd_check_option(uci
, s
, src_mac
, mac
);
544 fwd_check_option(uci
, s
, src_port
, portrange
);
545 fwd_check_option(uci
, s
, src_dport
, portrange
);
546 fwd_check_option(uci
, s
, dest_ip
, cidr
);
547 fwd_check_option(uci
, s
, dest_port
, portrange
);
548 fwd_check_option(uci
, s
, proto
, proto
);
550 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
552 dtn
->section
.redirect
.proto
= proto
;
553 dtn
->section
.redirect
.src
= zsrc
;
554 dtn
->section
.redirect
.src_ip
= src_ip
;
555 dtn
->section
.redirect
.src_mac
= src_mac
;
556 dtn
->section
.redirect
.src_port
= src_port
;
557 dtn
->section
.redirect
.src_dport
= src_dport
;
558 dtn
->section
.redirect
.dest_ip
= dest_ip
;
559 dtn
->section
.redirect
.dest_port
= dest_port
;
561 dtn
->type
= FWD_S_REDIRECT
;
562 dtn
->next
= cv
->cursor
;
567 fwd_read_error("out of memory while parsing config!");
571 static struct fwd_data
*
572 fwd_read_redirects(struct uci_context
*uci
, struct fwd_data
*zones
)
574 struct fwd_data_conveyor cv
;
579 ucix_for_each_section_type(uci
, "firewall", "redirect",
580 (void *)fwd_read_redirects_cb
, &cv
);
589 static void fwd_read_rules_cb(
590 struct uci_context
*uci
,
591 const char *s
, struct fwd_data_conveyor
*cv
593 const char *src
, *dest
;
594 struct fwd_data
*dtn
= NULL
;
595 struct fwd_zone
*zsrc
= NULL
;
596 struct fwd_zone
*zdest
= NULL
;
599 if( !(src
= fwd_read_string(uci
, s
, "src")) )
601 "section '%s' is missing 'src' option!",
605 else if( !(zsrc
= fwd_lookup_zone(cv
->head
, src
)) )
607 "section '%s' references unknown src zone '%s'!",
611 if( (dest
= fwd_read_string(uci
, s
, "dest")) != NULL
)
612 if( !(zdest
= fwd_lookup_zone(cv
->head
, dest
)) )
614 "section '%s' references unknown dest zone '%s'!",
618 /* uci context, section, name, type */
619 fwd_check_option(uci
, s
, src_ip
, cidr
);
620 fwd_check_option(uci
, s
, src_mac
, mac
);
621 fwd_check_option(uci
, s
, src_port
, portrange
);
622 fwd_check_option(uci
, s
, dest_ip
, cidr
);
623 fwd_check_option(uci
, s
, dest_port
, portrange
);
624 fwd_check_option(uci
, s
, proto
, proto
);
625 fwd_check_option(uci
, s
, icmptype
, icmptype
);
627 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
629 dtn
->section
.rule
.proto
= proto
;
630 dtn
->section
.rule
.icmp_type
= icmptype
;
631 dtn
->section
.rule
.src
= zsrc
;
632 dtn
->section
.rule
.src_ip
= src_ip
;
633 dtn
->section
.rule
.src_mac
= src_mac
;
634 dtn
->section
.rule
.src_port
= src_port
;
635 dtn
->section
.rule
.dest
= zdest
;
636 dtn
->section
.rule
.dest_ip
= dest_ip
;
637 dtn
->section
.rule
.dest_port
= dest_port
;
638 dtn
->section
.rule
.target
= fwd_read_policy(uci
, s
, "target");
640 dtn
->type
= FWD_S_RULE
;
641 dtn
->next
= cv
->cursor
;
646 fwd_read_error("out of memory while parsing config!");
650 static struct fwd_data
*
651 fwd_read_rules(struct uci_context
*uci
, struct fwd_data
*zones
)
653 struct fwd_data_conveyor cv
;
658 ucix_for_each_section_type(uci
, "firewall", "rule",
659 (void *)fwd_read_rules_cb
, &cv
);
668 static void fwd_read_includes_cb(
669 struct uci_context
*uci
,
670 const char *s
, struct fwd_data_conveyor
*cv
672 const char *path
= fwd_read_string(uci
, s
, "path");
673 struct fwd_data
*dtn
= NULL
;
677 if( (dtn
= fwd_alloc_ptr(struct fwd_data
)) != NULL
)
679 dtn
->section
.include
.path
= strdup(path
);
681 dtn
->type
= FWD_S_INCLUDE
;
682 dtn
->next
= cv
->cursor
;
687 fwd_read_error("out of memory while parsing config!");
692 static struct fwd_data
*
693 fwd_read_includes(struct uci_context
*uci
)
695 struct fwd_data_conveyor cv
;
700 ucix_for_each_section_type(uci
, "firewall", "include",
701 (void *)fwd_read_includes_cb
, &cv
);
710 static void fwd_read_network_data(
711 struct uci_context
*uci
, struct fwd_network_list
*net
713 struct fwd_network_list
*e
;
714 const char *type
, *ifname
;
716 for( e
= net
; e
; e
= e
->next
)
718 if( (type
= ucix_get_option(uci
, "network", e
->name
, NULL
)) != NULL
)
720 if( !(ifname
= ucix_get_option(uci
, "network", e
->name
, "ifname")) )
722 "section '%s' is missing 'ifname' option!",
726 e
->isalias
= (strcmp(type
, "alias") ? 0 : 1);
727 e
->ifname
= strdup(ifname
);
732 static void fwd_read_networks(
733 struct uci_context
*uci
, struct fwd_data
*zones
737 for( e
= zones
; e
; e
= e
->next
)
738 if( e
->type
== FWD_S_ZONE
)
739 fwd_read_network_data(uci
, e
->section
.zone
.networks
);
742 static void fwd_free_networks(struct fwd_network_list
*h
)
744 struct fwd_network_list
*e
= h
;
750 fwd_free_ptr(h
->name
);
751 fwd_free_ptr(h
->ifname
);
752 fwd_free_ptr(h
->addr
);
763 struct fwd_data
* fwd_read_config(void)
765 struct uci_context
*ctx
;
766 struct fwd_data
*defaults
, *zones
;
768 if( (ctx
= ucix_init("firewall")) != NULL
)
770 if( !(defaults
= fwd_read_defaults(ctx
)) )
773 if( !(zones
= fwd_read_zones(ctx
, defaults
)) )
776 fwd_append_config(defaults
, zones
);
777 fwd_append_config(defaults
, fwd_read_forwards(ctx
, zones
));
778 fwd_append_config(defaults
, fwd_read_redirects(ctx
, zones
));
779 fwd_append_config(defaults
, fwd_read_rules(ctx
, zones
));
780 fwd_append_config(defaults
, fwd_read_includes(ctx
));
784 if( (ctx
= ucix_init("network")) != NULL
)
786 fwd_read_networks(ctx
, zones
);
794 if( ctx
) ucix_cleanup(ctx
);
795 fwd_free_config(defaults
);
796 fwd_free_config(zones
);
801 void fwd_free_config(struct fwd_data
*h
)
803 struct fwd_data
*e
= h
;
812 fwd_free_ptr(h
->section
.include
.path
);
816 fwd_free_ptr(h
->section
.zone
.name
);
817 fwd_free_networks(h
->section
.zone
.networks
);
821 fwd_free_ptr(h
->section
.redirect
.src_ip
);
822 fwd_free_ptr(h
->section
.redirect
.src_mac
);
823 fwd_free_ptr(h
->section
.redirect
.src_port
);
824 fwd_free_ptr(h
->section
.redirect
.src_dport
);
825 fwd_free_ptr(h
->section
.redirect
.dest_ip
);
826 fwd_free_ptr(h
->section
.redirect
.dest_port
);
827 fwd_free_ptr(h
->section
.redirect
.proto
);
831 fwd_free_ptr(h
->section
.rule
.src_ip
);
832 fwd_free_ptr(h
->section
.rule
.src_mac
);
833 fwd_free_ptr(h
->section
.rule
.src_port
);
834 fwd_free_ptr(h
->section
.rule
.dest_ip
);
835 fwd_free_ptr(h
->section
.rule
.dest_port
);
836 fwd_free_ptr(h
->section
.rule
.proto
);
837 fwd_free_ptr(h
->section
.rule
.icmp_type
);
855 fwd_lookup_zone(struct fwd_data
*h
, const char *n
)
861 for( e
= h
; e
; e
= e
->next
)
863 if( (e
->type
= FWD_S_ZONE
) && !strcmp(e
->section
.zone
.name
, n
) )
864 return &e
->section
.zone
;