├── CMakeLists.txt ├── ubus.h ├── snats.h ├── forwards.h ├── rules.h ├── includes.h ├── redirects.h ├── defaults.h ├── helpers.h ├── zones.h ├── ipsets.h ├── helpers.conf ├── xtables-10.h ├── icmp_codes.h ├── utils.h ├── iptables.h ├── xtables-5.h ├── forwards.c ├── includes.c ├── helpers.c ├── ubus.c ├── snats.c ├── defaults.c ├── options.h ├── main.c ├── ipsets.c ├── rules.c ├── utils.c └── redirects.c /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | PROJECT(firewall3 C) 4 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations -Wno-format-truncation) 5 | 6 | SET(iptc_libs ip4tc) 7 | 8 | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 9 | 10 | IF (NOT DISABLE_STATIC_EXTENSIONS) 11 | LIST(APPEND ext_libs iptext) 12 | LIST(APPEND ext_libs iptext4) 13 | IF (NOT DISABLE_IPV6) 14 | LIST(APPEND ext_libs iptext6) 15 | ENDIF() 16 | ELSE() 17 | ADD_DEFINITIONS(-DDISABLE_STATIC_EXTENSIONS) 18 | ENDIF() 19 | 20 | IF (NOT DISABLE_IPV6) 21 | LIST(APPEND iptc_libs ip6tc) 22 | ELSE() 23 | ADD_DEFINITIONS(-DDISABLE_IPV6) 24 | ENDIF() 25 | 26 | FIND_PATH(uci_include_dir uci.h) 27 | INCLUDE_DIRECTORIES(${uci_include_dir}) 28 | 29 | ADD_EXECUTABLE(firewall3 main.c options.c defaults.c zones.c forwards.c rules.c redirects.c snats.c utils.c ubus.c ipsets.c includes.c iptables.c helpers.c) 30 | TARGET_LINK_LIBRARIES(firewall3 uci ubox ubus xtables m dl ${iptc_libs} ${ext_libs}) 31 | 32 | SET(CMAKE_INSTALL_PREFIX /usr) 33 | 34 | INSTALL(TARGETS firewall3 RUNTIME DESTINATION sbin) 35 | -------------------------------------------------------------------------------- /ubus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_UBUS_H 20 | #define __FW3_UBUS_H 21 | 22 | #include 23 | #include 24 | 25 | #include "options.h" 26 | 27 | 28 | bool fw3_ubus_connect(void); 29 | void fw3_ubus_disconnect(void); 30 | 31 | struct fw3_device * fw3_ubus_device(const char *net); 32 | 33 | int fw3_ubus_address(struct list_head *list, const char *net); 34 | 35 | void fw3_ubus_zone_devices(struct fw3_zone *zone); 36 | 37 | void fw3_ubus_rules(struct blob_buf *b); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /snats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2014 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_SNATS_H 20 | #define __FW3_SNATS_H 21 | 22 | #include "options.h" 23 | #include "zones.h" 24 | #include "ipsets.h" 25 | #include "ubus.h" 26 | #include "iptables.h" 27 | 28 | extern const struct fw3_option fw3_snat_opts[]; 29 | 30 | void fw3_load_snats(struct fw3_state *state, struct uci_package *p, struct blob_attr *a); 31 | void fw3_print_snats(struct fw3_ipt_handle *handle, struct fw3_state *state); 32 | 33 | static inline void fw3_free_snat(struct fw3_snat *snat) 34 | { 35 | list_del(&snat->list); 36 | fw3_free_object(snat, fw3_snat_opts); 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /forwards.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_FORWARDS_H 20 | #define __FW3_FORWARDS_H 21 | 22 | #include "options.h" 23 | #include "zones.h" 24 | #include "utils.h" 25 | #include "iptables.h" 26 | 27 | extern const struct fw3_option fw3_forward_opts[]; 28 | 29 | void fw3_load_forwards(struct fw3_state *state, struct uci_package *p, struct blob_attr *a); 30 | void fw3_print_forwards(struct fw3_ipt_handle *handle, struct fw3_state *state); 31 | 32 | static inline void fw3_free_forward(struct fw3_forward *forward) 33 | { 34 | list_del(&forward->list); 35 | fw3_free_object(forward, fw3_forward_opts); 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /rules.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_RULES_H 20 | #define __FW3_RULES_H 21 | 22 | #include "options.h" 23 | #include "zones.h" 24 | #include "ipsets.h" 25 | #include "helpers.h" 26 | #include "utils.h" 27 | #include "iptables.h" 28 | 29 | extern const struct fw3_option fw3_rule_opts[]; 30 | 31 | void fw3_load_rules(struct fw3_state *state, struct uci_package *p, struct blob_attr *a); 32 | void fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state); 33 | 34 | static inline void fw3_free_rule(struct fw3_rule *rule) 35 | { 36 | list_del(&rule->list); 37 | fw3_free_object(rule, fw3_rule_opts); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /includes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_INCLUDES_H 20 | #define __FW3_INCLUDES_H 21 | 22 | #include "options.h" 23 | #include "utils.h" 24 | 25 | extern const struct fw3_option fw3_include_opts[]; 26 | 27 | void fw3_load_includes(struct fw3_state *state, struct uci_package *p, struct blob_attr *a); 28 | 29 | void fw3_print_includes(struct fw3_state *state, enum fw3_family family, 30 | bool reload); 31 | 32 | void fw3_run_includes(struct fw3_state *state, bool reload); 33 | 34 | static inline void fw3_free_include(struct fw3_include *include) 35 | { 36 | list_del(&include->list); 37 | fw3_free_object(include, fw3_include_opts); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /redirects.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_REDIRECTS_H 20 | #define __FW3_REDIRECTS_H 21 | 22 | #include "options.h" 23 | #include "zones.h" 24 | #include "ipsets.h" 25 | #include "helpers.h" 26 | #include "ubus.h" 27 | #include "iptables.h" 28 | 29 | extern const struct fw3_option fw3_redirect_opts[]; 30 | 31 | void fw3_load_redirects(struct fw3_state *state, struct uci_package *p, 32 | struct blob_attr *a); 33 | void fw3_print_redirects(struct fw3_ipt_handle *handle, 34 | struct fw3_state *state); 35 | 36 | static inline void fw3_free_redirect(struct fw3_redirect *redir) 37 | { 38 | list_del(&redir->list); 39 | fw3_free_object(redir, fw3_redirect_opts); 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_DEFAULTS_H 20 | #define __FW3_DEFAULTS_H 21 | 22 | #include "options.h" 23 | #include "iptables.h" 24 | 25 | extern const struct fw3_option fw3_flag_opts[]; 26 | 27 | void fw3_load_defaults(struct fw3_state *state, struct uci_package *p); 28 | 29 | void fw3_print_default_chains(struct fw3_ipt_handle *handle, 30 | struct fw3_state *state, bool reload); 31 | 32 | void fw3_print_default_head_rules(struct fw3_ipt_handle *handle, 33 | struct fw3_state *state, bool reload); 34 | 35 | void fw3_print_default_tail_rules(struct fw3_ipt_handle *handle, 36 | struct fw3_state *state, bool reload); 37 | 38 | void fw3_set_defaults(struct fw3_state *state); 39 | 40 | void fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, 41 | bool reload); 42 | 43 | void fw3_flush_all(struct fw3_ipt_handle *handle); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2018 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_HELPERS_H 20 | #define __FW3_HELPERS_H 21 | 22 | #include "options.h" 23 | #include "utils.h" 24 | #include "iptables.h" 25 | 26 | 27 | extern const struct fw3_option fw3_cthelper_opts[]; 28 | 29 | void 30 | fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p); 31 | 32 | struct fw3_cthelper * 33 | fw3_lookup_cthelper(struct fw3_state *state, const char *name); 34 | 35 | struct fw3_cthelper * 36 | fw3_lookup_cthelper_by_proto_port(struct fw3_state *state, 37 | struct fw3_protocol *proto, 38 | struct fw3_port *port); 39 | 40 | void 41 | fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state, 42 | struct fw3_zone *zone); 43 | 44 | bool 45 | fw3_cthelper_check_proto(const struct fw3_cthelper *h, const struct fw3_protocol *proto); 46 | 47 | static inline void fw3_free_cthelper(struct fw3_cthelper *helper) 48 | { 49 | list_del(&helper->list); 50 | fw3_free_object(helper, fw3_cthelper_opts); 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /zones.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_ZONES_H 20 | #define __FW3_ZONES_H 21 | 22 | #include "options.h" 23 | #include "iptables.h" 24 | 25 | /* XT_EXTENSION_MAXNAMELEN (29) 26 | * - sizeof("postrouting_") 27 | * - sizeof("_rule") 28 | * - sizeof("\0") 29 | */ 30 | #define FW3_ZONE_MAXNAMELEN 11 31 | 32 | extern const struct fw3_option fw3_zone_opts[]; 33 | 34 | struct fw3_zone * fw3_alloc_zone(void); 35 | 36 | void fw3_load_zones(struct fw3_state *state, struct uci_package *p); 37 | 38 | void fw3_print_zone_chains(struct fw3_ipt_handle *handle, 39 | struct fw3_state *state, bool reload); 40 | 41 | void fw3_print_zone_rules(struct fw3_ipt_handle *handle, 42 | struct fw3_state *state, bool reload); 43 | 44 | void fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state, 45 | bool reload); 46 | 47 | void fw3_hotplug_zones(struct fw3_state *state, bool add); 48 | 49 | struct fw3_zone * fw3_lookup_zone(struct fw3_state *state, const char *name); 50 | 51 | struct list_head * fw3_resolve_zone_addresses(struct fw3_zone *zone, 52 | struct fw3_address *addr); 53 | 54 | #define fw3_free_zone(zone) \ 55 | fw3_free_object(zone, fw3_zone_opts) 56 | 57 | #define fw3_to_src_target(t) \ 58 | (FW3_FLAG_SRC_ACCEPT - FW3_FLAG_ACCEPT + t) 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /ipsets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_IPSETS_H 20 | #define __FW3_IPSETS_H 21 | 22 | #include 23 | 24 | #include "options.h" 25 | #include "utils.h" 26 | 27 | 28 | extern const struct fw3_option fw3_ipset_opts[]; 29 | 30 | void fw3_load_ipsets(struct fw3_state *state, struct uci_package *p, struct blob_attr *a); 31 | void fw3_create_ipsets(struct fw3_state *state, enum fw3_family family, 32 | bool reload_set); 33 | void fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family, 34 | bool reload_set); 35 | 36 | struct fw3_ipset * fw3_lookup_ipset(struct fw3_state *state, const char *name); 37 | 38 | bool fw3_check_ipset(struct fw3_ipset *set); 39 | 40 | void 41 | fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state, 42 | struct fw3_state *cfg_state); 43 | 44 | static inline void fw3_free_ipset(struct fw3_ipset *ipset) 45 | { 46 | list_del(&ipset->list); 47 | fw3_free_object(ipset, fw3_ipset_opts); 48 | } 49 | 50 | #ifndef SO_IP_SET 51 | 52 | #define SO_IP_SET 83 53 | #define IPSET_MAXNAMELEN 32 54 | #define IPSET_INVALID_ID 65535 55 | 56 | union ip_set_name_index { 57 | char name[IPSET_MAXNAMELEN]; 58 | uint16_t index; 59 | }; 60 | 61 | #define IP_SET_OP_GET_BYNAME 0x00000006 62 | struct ip_set_req_get_set { 63 | uint32_t op; 64 | uint32_t version; 65 | union ip_set_name_index set; 66 | }; 67 | 68 | #define IP_SET_OP_VERSION 0x00000100 69 | struct ip_set_req_version { 70 | uint32_t op; 71 | uint32_t version; 72 | }; 73 | 74 | #endif /* SO_IP_SET */ 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /helpers.conf: -------------------------------------------------------------------------------- 1 | config helper 2 | option name 'amanda' 3 | option description 'Amanda backup and archiving proto' 4 | option module 'nf_conntrack_amanda' 5 | option family 'any' 6 | option proto 'udp' 7 | option port '10080' 8 | 9 | config helper 10 | option name 'ftp' 11 | option description 'FTP passive connection tracking' 12 | option module 'nf_conntrack_ftp' 13 | option family 'any' 14 | option proto 'tcp' 15 | option port '21' 16 | 17 | config helper 18 | option name 'RAS' 19 | option description 'RAS proto tracking' 20 | option module 'nf_conntrack_h323' 21 | option family 'any' 22 | option proto 'udp' 23 | option port '1719' 24 | 25 | config helper 26 | option name 'Q.931' 27 | option description 'Q.931 proto tracking' 28 | option module 'nf_conntrack_h323' 29 | option family 'any' 30 | option proto 'tcp' 31 | option port '1720' 32 | 33 | config helper 34 | option name 'irc' 35 | option description 'IRC DCC connection tracking' 36 | option module 'nf_conntrack_irc' 37 | option family 'ipv4' 38 | option proto 'tcp' 39 | option port '6667' 40 | 41 | config helper 42 | option name 'netbios-ns' 43 | option description 'NetBIOS name service broadcast tracking' 44 | option module 'nf_conntrack_netbios_ns' 45 | option family 'ipv4' 46 | option proto 'udp' 47 | option port '137' 48 | 49 | config helper 50 | option name 'pptp' 51 | option description 'PPTP VPN connection tracking' 52 | option module 'nf_conntrack_pptp' 53 | option family 'ipv4' 54 | option proto 'tcp' 55 | option port '1723' 56 | 57 | config helper 58 | option name 'sane' 59 | option description 'SANE scanner connection tracking' 60 | option module 'nf_conntrack_sane' 61 | option family 'any' 62 | option proto 'tcp' 63 | option port '6566' 64 | 65 | config helper 66 | option name 'sip' 67 | option description 'SIP VoIP connection tracking' 68 | option module 'nf_conntrack_sip' 69 | option family 'any' 70 | option proto 'tcpudp' 71 | option port '5060' 72 | 73 | config helper 74 | option name 'snmp' 75 | option description 'SNMP monitoring connection tracking' 76 | option module 'nf_conntrack_snmp' 77 | option family 'ipv4' 78 | option proto 'udp' 79 | option port '161' 80 | 81 | config helper 82 | option name 'tftp' 83 | option description 'TFTP connection tracking' 84 | option module 'nf_conntrack_tftp' 85 | option family 'any' 86 | option proto 'udp' 87 | option port '69' 88 | 89 | config helper 90 | option name 'rtsp' 91 | option description 'RTSP connection tracking' 92 | option module 'nf_conntrack_rtsp' 93 | option family 'ipv4' 94 | option proto 'tcp' 95 | option port '554' 96 | -------------------------------------------------------------------------------- /xtables-10.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_XTABLES_10_H 20 | #define __FW3_XTABLES_10_H 21 | 22 | extern struct xtables_match *xtables_pending_matches; 23 | extern struct xtables_target *xtables_pending_targets; 24 | 25 | static inline void 26 | fw3_xt_reset(void) 27 | { 28 | xtables_matches = NULL; 29 | xtables_targets = NULL; 30 | 31 | xtables_pending_matches = NULL; 32 | xtables_pending_targets = NULL; 33 | } 34 | 35 | 36 | static inline const char * 37 | fw3_xt_get_match_name(struct xtables_match *m) 38 | { 39 | if (m->alias) 40 | return m->alias(m->m); 41 | 42 | return m->m->u.user.name; 43 | } 44 | 45 | static inline void 46 | fw3_xt_set_match_name(struct xtables_match *m) 47 | { 48 | snprintf(m->m->u.user.name, sizeof(m->m->u.user.name), "%s", 49 | m->real_name ? m->real_name : m->name); 50 | } 51 | 52 | static inline bool 53 | fw3_xt_has_match_parse(struct xtables_match *m) 54 | { 55 | return (m->parse || m->x6_parse); 56 | } 57 | 58 | static inline void 59 | fw3_xt_free_match_udata(struct xtables_match *m) 60 | { 61 | if (m->udata_size) 62 | { 63 | free(m->udata); 64 | m->udata = fw3_alloc(m->udata_size); 65 | } 66 | } 67 | 68 | static inline void 69 | fw3_xt_merge_match_options(struct xtables_globals *g, struct xtables_match *m) 70 | { 71 | if (m->x6_options) 72 | g->opts = xtables_options_xfrm(g->orig_opts, g->opts, 73 | m->x6_options, &m->option_offset); 74 | 75 | if (m->extra_opts) 76 | g->opts = xtables_merge_options(g->orig_opts, g->opts, 77 | m->extra_opts, &m->option_offset); 78 | } 79 | 80 | 81 | static inline const char * 82 | fw3_xt_get_target_name(struct xtables_target *t) 83 | { 84 | if (t->alias) 85 | return t->alias(t->t); 86 | 87 | return t->t->u.user.name; 88 | } 89 | 90 | static inline void 91 | fw3_xt_set_target_name(struct xtables_target *t, const char *name) 92 | { 93 | snprintf(t->t->u.user.name, sizeof(t->t->u.user.name), "%s", 94 | t->real_name ? t->real_name : name); 95 | } 96 | 97 | static inline bool 98 | fw3_xt_has_target_parse(struct xtables_target *t) 99 | { 100 | return (t->parse || t->x6_parse); 101 | } 102 | 103 | static inline void 104 | fw3_xt_free_target_udata(struct xtables_target *t) 105 | { 106 | if (t->udata_size) 107 | { 108 | free(t->udata); 109 | t->udata = fw3_alloc(t->udata_size); 110 | } 111 | } 112 | 113 | static inline void 114 | fw3_xt_merge_target_options(struct xtables_globals *g, struct xtables_target *t) 115 | { 116 | if (t->x6_options) 117 | g->opts = xtables_options_xfrm(g->orig_opts, g->opts, 118 | t->x6_options, &t->option_offset); 119 | else 120 | g->opts = xtables_merge_options(g->orig_opts, g->opts, 121 | t->extra_opts, &t->option_offset); 122 | } 123 | 124 | static inline void 125 | fw3_xt_print_matches(void *ip, struct xtables_rule_match *matches) 126 | { 127 | struct xtables_rule_match *rm; 128 | struct xtables_match *m; 129 | 130 | for (rm = matches; rm; rm = rm->next) 131 | { 132 | m = rm->match; 133 | printf(" -m %s", fw3_xt_get_match_name(m)); 134 | 135 | if (m->save) 136 | m->save(ip, m->m); 137 | } 138 | } 139 | 140 | static inline void 141 | fw3_xt_print_target(void *ip, struct xtables_target *target) 142 | { 143 | if (target) 144 | { 145 | printf(" -j %s", fw3_xt_get_target_name(target)); 146 | 147 | if (target->save) 148 | target->save(ip, target->t); 149 | } 150 | } 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /icmp_codes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_ICMP_CODES_H 20 | #define __FW3_ICMP_CODES_H 21 | 22 | 23 | struct fw3_icmptype_entry { 24 | const char *name; 25 | uint8_t type; 26 | uint8_t code_min; 27 | uint8_t code_max; 28 | }; 29 | 30 | /* taken from iptables extensions/libipt_icmp.c */ 31 | static const struct fw3_icmptype_entry fw3_icmptype_list_v4[] = { 32 | { "any", 0xFF, 0, 0xFF }, 33 | { "echo-reply", 0, 0, 0xFF }, 34 | /* Alias */ { "pong", 0, 0, 0xFF }, 35 | 36 | { "destination-unreachable", 3, 0, 0xFF }, 37 | { "network-unreachable", 3, 0, 0 }, 38 | { "host-unreachable", 3, 1, 1 }, 39 | { "protocol-unreachable", 3, 2, 2 }, 40 | { "port-unreachable", 3, 3, 3 }, 41 | { "fragmentation-needed", 3, 4, 4 }, 42 | { "source-route-failed", 3, 5, 5 }, 43 | { "network-unknown", 3, 6, 6 }, 44 | { "host-unknown", 3, 7, 7 }, 45 | { "network-prohibited", 3, 9, 9 }, 46 | { "host-prohibited", 3, 10, 10 }, 47 | { "TOS-network-unreachable", 3, 11, 11 }, 48 | { "TOS-host-unreachable", 3, 12, 12 }, 49 | { "communication-prohibited", 3, 13, 13 }, 50 | { "host-precedence-violation", 3, 14, 14 }, 51 | { "precedence-cutoff", 3, 15, 15 }, 52 | 53 | { "source-quench", 4, 0, 0xFF }, 54 | 55 | { "redirect", 5, 0, 0xFF }, 56 | { "network-redirect", 5, 0, 0 }, 57 | { "host-redirect", 5, 1, 1 }, 58 | { "TOS-network-redirect", 5, 2, 2 }, 59 | { "TOS-host-redirect", 5, 3, 3 }, 60 | 61 | { "echo-request", 8, 0, 0xFF }, 62 | /* Alias */ { "ping", 8, 0, 0xFF }, 63 | 64 | { "router-advertisement", 9, 0, 0xFF }, 65 | 66 | { "router-solicitation", 10, 0, 0xFF }, 67 | 68 | { "time-exceeded", 11, 0, 0xFF }, 69 | /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, 70 | { "ttl-zero-during-transit", 11, 0, 0 }, 71 | { "ttl-zero-during-reassembly", 11, 1, 1 }, 72 | 73 | { "parameter-problem", 12, 0, 0xFF }, 74 | { "ip-header-bad", 12, 0, 0 }, 75 | { "required-option-missing", 12, 1, 1 }, 76 | 77 | { "timestamp-request", 13, 0, 0xFF }, 78 | 79 | { "timestamp-reply", 14, 0, 0xFF }, 80 | 81 | { "address-mask-request", 17, 0, 0xFF }, 82 | 83 | { "address-mask-reply", 18, 0, 0xFF } 84 | }; 85 | 86 | /* taken from iptables extensions/libip6t_icmp6.c */ 87 | static const struct fw3_icmptype_entry fw3_icmptype_list_v6[] = { 88 | { "destination-unreachable", 1, 0, 0xFF }, 89 | { "no-route", 1, 0, 0 }, 90 | { "communication-prohibited", 1, 1, 1 }, 91 | { "address-unreachable", 1, 3, 3 }, 92 | { "port-unreachable", 1, 4, 4 }, 93 | 94 | { "packet-too-big", 2, 0, 0xFF }, 95 | 96 | { "time-exceeded", 3, 0, 0xFF }, 97 | /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, 98 | { "ttl-zero-during-transit", 3, 0, 0 }, 99 | { "ttl-zero-during-reassembly", 3, 1, 1 }, 100 | 101 | { "parameter-problem", 4, 0, 0xFF }, 102 | { "bad-header", 4, 0, 0 }, 103 | { "unknown-header-type", 4, 1, 1 }, 104 | { "unknown-option", 4, 2, 2 }, 105 | 106 | { "echo-request", 128, 0, 0xFF }, 107 | /* Alias */ { "ping", 128, 0, 0xFF }, 108 | 109 | { "echo-reply", 129, 0, 0xFF }, 110 | /* Alias */ { "pong", 129, 0, 0xFF }, 111 | 112 | { "router-solicitation", 133, 0, 0xFF }, 113 | 114 | { "router-advertisement", 134, 0, 0xFF }, 115 | 116 | { "neighbour-solicitation", 135, 0, 0xFF }, 117 | /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, 118 | 119 | { "neighbour-advertisement", 136, 0, 0xFF }, 120 | /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, 121 | 122 | { "redirect", 137, 0, 0xFF }, 123 | }; 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_UTILS_H 20 | #define __FW3_UTILS_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | #define FW3_STATEFILE "/var/run/fw3.state" 41 | #define FW3_LOCKFILE "/var/run/fw3.lock" 42 | #define FW3_HELPERCONF "/usr/share/fw3/helpers.conf" 43 | #define FW3_HOTPLUG "/sbin/hotplug-call" 44 | 45 | extern bool fw3_pr_debug; 46 | 47 | struct fw3_address; 48 | 49 | void warn_elem(struct uci_element *e, const char *format, ...) 50 | __attribute__ ((format (printf, 2, 3))); 51 | void warn(const char *format, ...) 52 | __attribute__ ((format (printf, 1, 2))); 53 | void error(const char *format, ...) 54 | __attribute__ ((format (printf, 1, 2))); 55 | void info(const char *format, ...) 56 | __attribute__ ((format (printf, 1, 2))); 57 | 58 | #define warn_section(t, r, e, fmt, ...) \ 59 | do { \ 60 | if (e) \ 61 | warn_elem(e, fmt, ##__VA_ARGS__); \ 62 | else \ 63 | warn("Warning: ubus " t " (%s) " fmt, \ 64 | (r && r->name) ? r->name : "?", ##__VA_ARGS__); \ 65 | } while(0) 66 | 67 | #define fw3_setbit(field, flag) field |= (1 << (flag)) 68 | #define fw3_delbit(field, flag) field &= ~(1 << (flag)) 69 | #define fw3_hasbit(field, flag) (field & (1 << (flag))) 70 | 71 | #define set(field, family, flag) fw3_setbit(field[family == FW3_FAMILY_V6], flag) 72 | #define del(field, family, flag) fw3_delbit(field[family == FW3_FAMILY_V6], flag) 73 | #define has(field, family, flag) fw3_hasbit(field[family == FW3_FAMILY_V6], flag) 74 | 75 | #define fw3_foreach(p, h) \ 76 | for (p = list_empty(h) ? NULL : list_first_entry(h, typeof(*p), list); \ 77 | list_empty(h) ? (p == NULL) : (&p->list != (h)); \ 78 | p = list_empty(h) ? list_first_entry(h, typeof(*p), list) \ 79 | : list_entry(p->list.next, typeof(*p), list)) 80 | 81 | #define fw3_is_family(p, f) \ 82 | (!p || (p)->family == FW3_FAMILY_ANY || (p)->family == f) 83 | 84 | #define fw3_no_family(flags) \ 85 | (!(flags & ((1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6)))) 86 | 87 | #define fw3_no_table(flags) \ 88 | (!(flags & ((1< 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_IPTABLES_H 20 | #define __FW3_IPTABLES_H 21 | 22 | #ifndef DISABLE_STATIC_EXTENSIONS 23 | /* libipt*ext.so interfaces */ 24 | extern void init_extensions(void); 25 | extern void init_extensions4(void); 26 | extern void init_extensions6(void); 27 | #else 28 | static inline void init_extensions(void) { } 29 | static inline void init_extensions4(void) { } 30 | static inline void init_extensions6(void) { } 31 | #endif 32 | 33 | /* Required by certain extensions like SNAT and DNAT */ 34 | extern int kernel_version; 35 | void get_kernel_version(void); 36 | 37 | struct fw3_ipt_handle { 38 | enum fw3_family family; 39 | enum fw3_table table; 40 | void *handle; 41 | }; 42 | 43 | struct fw3_ipt_rule; 44 | 45 | struct fw3_ipt_handle *fw3_ipt_open(enum fw3_family family, 46 | enum fw3_table table); 47 | 48 | void fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain, 49 | enum fw3_flag policy); 50 | 51 | 52 | void fw3_ipt_flush_chain(struct fw3_ipt_handle *h, const char *chain); 53 | void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, bool if_unused, 54 | const char *chain); 55 | 56 | void fw3_ipt_delete_id_rules(struct fw3_ipt_handle *h, const char *chain); 57 | 58 | void fw3_ipt_create_chain(struct fw3_ipt_handle *h, bool ignore_existing, 59 | const char *chain); 60 | 61 | void fw3_ipt_flush(struct fw3_ipt_handle *h); 62 | 63 | void fw3_ipt_gc(struct fw3_ipt_handle *h); 64 | 65 | void fw3_ipt_commit(struct fw3_ipt_handle *h); 66 | 67 | void fw3_ipt_close(struct fw3_ipt_handle *h); 68 | 69 | struct fw3_ipt_rule *fw3_ipt_rule_new(struct fw3_ipt_handle *h); 70 | 71 | void fw3_ipt_rule_proto(struct fw3_ipt_rule *r, struct fw3_protocol *proto); 72 | 73 | void fw3_ipt_rule_in_out(struct fw3_ipt_rule *r, 74 | struct fw3_device *in, struct fw3_device *out); 75 | 76 | void fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r, 77 | struct fw3_address *src, struct fw3_address *dest); 78 | 79 | void fw3_ipt_rule_sport_dport(struct fw3_ipt_rule *r, 80 | struct fw3_port *sp, struct fw3_port *dp); 81 | 82 | void fw3_ipt_rule_device(struct fw3_ipt_rule *r, const char *device, bool out); 83 | 84 | void fw3_ipt_rule_mac(struct fw3_ipt_rule *r, struct fw3_mac *mac); 85 | 86 | void fw3_ipt_rule_icmptype(struct fw3_ipt_rule *r, struct fw3_icmptype *icmp); 87 | 88 | void fw3_ipt_rule_limit(struct fw3_ipt_rule *r, struct fw3_limit *limit); 89 | 90 | void fw3_ipt_rule_ipset(struct fw3_ipt_rule *r, struct fw3_setmatch *match); 91 | 92 | void fw3_ipt_rule_helper(struct fw3_ipt_rule *r, struct fw3_cthelpermatch *match); 93 | 94 | void fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time); 95 | 96 | void fw3_ipt_rule_mark(struct fw3_ipt_rule *r, struct fw3_mark *mark); 97 | 98 | void fw3_ipt_rule_dscp(struct fw3_ipt_rule *r, struct fw3_dscp *dscp); 99 | 100 | void fw3_ipt_rule_comment(struct fw3_ipt_rule *r, const char *fmt, ...); 101 | 102 | void fw3_ipt_rule_extra(struct fw3_ipt_rule *r, const char *extra); 103 | 104 | void fw3_ipt_rule_addarg(struct fw3_ipt_rule *r, bool inv, 105 | const char *k, const char *v); 106 | 107 | struct fw3_ipt_rule * fw3_ipt_rule_create(struct fw3_ipt_handle *handle, 108 | struct fw3_protocol *proto, 109 | struct fw3_device *in, 110 | struct fw3_device *out, 111 | struct fw3_address *src, 112 | struct fw3_address *dest); 113 | 114 | void __fw3_ipt_rule_append(struct fw3_ipt_rule *r, bool repl, 115 | const char *fmt, ...); 116 | 117 | #define fw3_ipt_rule_append(rule, ...) \ 118 | __fw3_ipt_rule_append(rule, false, __VA_ARGS__) 119 | 120 | #define fw3_ipt_rule_replace(rule, ...) \ 121 | __fw3_ipt_rule_append(rule, true, __VA_ARGS__) 122 | 123 | static inline void 124 | fw3_ipt_rule_target(struct fw3_ipt_rule *r, const char *fmt, ...) 125 | { 126 | va_list ap; 127 | char buf[32]; 128 | 129 | va_start(ap, fmt); 130 | vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 131 | va_end(ap); 132 | 133 | fw3_ipt_rule_addarg(r, false, "-j", buf); 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /xtables-5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_XTABLES_5_H 20 | #define __FW3_XTABLES_5_H 21 | 22 | static inline void 23 | fw3_xt_reset(void) 24 | { 25 | xtables_matches = NULL; 26 | xtables_targets = NULL; 27 | } 28 | 29 | 30 | static inline const char * 31 | fw3_xt_get_match_name(struct xtables_match *m) 32 | { 33 | return m->m->u.user.name; 34 | } 35 | 36 | static inline void 37 | fw3_xt_set_match_name(struct xtables_match *m) 38 | { 39 | snprintf(m->m->u.user.name, sizeof(m->m->u.user.name), "%s", m->name); 40 | } 41 | 42 | static inline bool 43 | fw3_xt_has_match_parse(struct xtables_match *m) 44 | { 45 | return !!m->parse; 46 | } 47 | 48 | static inline void 49 | fw3_xt_free_match_udata(struct xtables_match *m) 50 | { 51 | return; 52 | } 53 | 54 | static inline void 55 | fw3_xt_merge_match_options(struct xtables_globals *g, struct xtables_match *m) 56 | { 57 | g->opts = xtables_merge_options(g->opts, m->extra_opts, &m->option_offset); 58 | } 59 | 60 | 61 | static inline const char * 62 | fw3_xt_get_target_name(struct xtables_target *t) 63 | { 64 | return t->t->u.user.name; 65 | } 66 | 67 | static inline void 68 | fw3_xt_set_target_name(struct xtables_target *t, const char *name) 69 | { 70 | snprintf(t->t->u.user.name, sizeof(t->t->u.user.name), "%s", name); 71 | } 72 | 73 | static inline bool 74 | fw3_xt_has_target_parse(struct xtables_target *t) 75 | { 76 | return !!t->parse; 77 | } 78 | 79 | static inline void 80 | fw3_xt_free_target_udata(struct xtables_target *t) 81 | { 82 | return; 83 | } 84 | 85 | static inline void 86 | fw3_xt_merge_target_options(struct xtables_globals *g, struct xtables_target *t) 87 | { 88 | g->opts = xtables_merge_options(g->opts, t->extra_opts, &t->option_offset); 89 | } 90 | 91 | static inline void 92 | fw3_xt_print_matches(void *ip, struct xtables_rule_match *matches) 93 | { 94 | struct xtables_rule_match *rm; 95 | struct xtables_match *m; 96 | 97 | printf(" "); 98 | 99 | for (rm = matches; rm; rm = rm->next) 100 | { 101 | m = rm->match; 102 | printf("-m %s ", fw3_xt_get_match_name(m)); 103 | 104 | if (m->save) 105 | m->save(ip, m->m); 106 | } 107 | } 108 | 109 | static inline void 110 | fw3_xt_print_target(void *ip, struct xtables_target *target) 111 | { 112 | if (target) 113 | { 114 | printf("-j %s ", fw3_xt_get_target_name(target)); 115 | 116 | if (target->save) 117 | target->save(ip, target->t); 118 | } 119 | } 120 | 121 | 122 | /* xtables api addons */ 123 | 124 | static inline void 125 | xtables_option_mpcall(unsigned int c, char **argv, bool invert, 126 | struct xtables_match *m, void *fw) 127 | { 128 | if (m->parse) 129 | m->parse(c - m->option_offset, argv, invert, &m->mflags, fw, &m->m); 130 | } 131 | 132 | static inline void 133 | xtables_option_mfcall(struct xtables_match *m) 134 | { 135 | if (m->final_check) 136 | m->final_check(m->mflags); 137 | } 138 | 139 | static inline void 140 | xtables_option_tpcall(unsigned int c, char **argv, bool invert, 141 | struct xtables_target *t, void *fw) 142 | { 143 | if (t->parse) 144 | t->parse(c - t->option_offset, argv, invert, &t->tflags, fw, &t->t); 145 | } 146 | 147 | static inline void 148 | xtables_option_tfcall(struct xtables_target *t) 149 | { 150 | if (t->final_check) 151 | t->final_check(t->tflags); 152 | } 153 | 154 | static inline void 155 | xtables_rule_matches_free(struct xtables_rule_match **matches) 156 | { 157 | struct xtables_rule_match *mp, *tmp; 158 | 159 | for (mp = *matches; mp;) 160 | { 161 | tmp = mp->next; 162 | 163 | if (mp->match->m) 164 | { 165 | free(mp->match->m); 166 | mp->match->m = NULL; 167 | } 168 | 169 | if (mp->match == mp->match->next) 170 | { 171 | free(mp->match); 172 | mp->match = NULL; 173 | } 174 | 175 | free(mp); 176 | mp = tmp; 177 | } 178 | 179 | *matches = NULL; 180 | } 181 | 182 | static inline int 183 | xtables_ipmask_to_cidr(const struct in_addr *mask) 184 | { 185 | int bits; 186 | uint32_t m; 187 | 188 | for (m = ntohl(mask->s_addr), bits = 0; m & 0x80000000; m <<= 1) 189 | bits++; 190 | 191 | return bits; 192 | } 193 | 194 | static inline int 195 | xtables_ip6mask_to_cidr(const struct in6_addr *mask) 196 | { 197 | int bits = 0; 198 | uint32_t a, b, c, d; 199 | 200 | a = ntohl(mask->s6_addr32[0]); 201 | b = ntohl(mask->s6_addr32[1]); 202 | c = ntohl(mask->s6_addr32[2]); 203 | d = ntohl(mask->s6_addr32[3]); 204 | 205 | while (a & 0x80000000U) 206 | { 207 | a <<= 1; 208 | a |= (b >> 31) & 1; 209 | b <<= 1; 210 | b |= (c >> 31) & 1; 211 | c <<= 1; 212 | c |= (d >> 31) & 1; 213 | d <<= 1; 214 | 215 | bits++; 216 | } 217 | 218 | return bits; 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /forwards.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "forwards.h" 20 | 21 | 22 | const struct fw3_option fw3_forward_opts[] = { 23 | FW3_OPT("enabled", bool, forward, enabled), 24 | 25 | FW3_OPT("name", string, forward, name), 26 | FW3_OPT("family", family, forward, family), 27 | 28 | FW3_OPT("src", device, forward, src), 29 | FW3_OPT("dest", device, forward, dest), 30 | 31 | { } 32 | }; 33 | 34 | static bool 35 | check_forward(struct fw3_state *state, struct fw3_forward *forward, struct uci_element *e) 36 | { 37 | if (!forward->enabled) 38 | return false; 39 | 40 | if (forward->src.invert || forward->dest.invert) 41 | { 42 | warn_section("forward", forward, e, "must not have inverted 'src' or 'dest' options"); 43 | return false; 44 | } 45 | else if (forward->src.set && !forward->src.any && 46 | !(forward->_src = fw3_lookup_zone(state, forward->src.name))) 47 | { 48 | warn_section("forward", forward, e, "refers to not existing zone '%s'", 49 | forward->src.name); 50 | return false; 51 | } 52 | else if (forward->dest.set && !forward->dest.any && 53 | !(forward->_dest = fw3_lookup_zone(state, forward->dest.name))) 54 | { 55 | warn_section("forward", forward, e, "refers to not existing zone '%s'", 56 | forward->dest.name); 57 | return false; 58 | } 59 | 60 | /* NB: forward family... */ 61 | if (forward->_dest) 62 | { 63 | fw3_setbit(forward->_dest->flags[0], FW3_FLAG_ACCEPT); 64 | fw3_setbit(forward->_dest->flags[1], FW3_FLAG_ACCEPT); 65 | } 66 | 67 | return true; 68 | } 69 | 70 | static struct fw3_forward * 71 | fw3_alloc_forward(struct fw3_state *state) 72 | { 73 | struct fw3_forward *forward; 74 | 75 | forward = calloc(1, sizeof(*forward)); 76 | if (!forward) 77 | return NULL; 78 | 79 | forward->enabled = true; 80 | 81 | list_add_tail(&forward->list, &state->forwards); 82 | 83 | return forward; 84 | } 85 | 86 | void 87 | fw3_load_forwards(struct fw3_state *state, struct uci_package *p, 88 | struct blob_attr *a) 89 | { 90 | struct uci_section *s; 91 | struct uci_element *e; 92 | struct fw3_forward *forward; 93 | struct blob_attr *entry; 94 | unsigned rem; 95 | 96 | INIT_LIST_HEAD(&state->forwards); 97 | 98 | blob_for_each_attr(entry, a, rem) 99 | { 100 | const char *type; 101 | const char *name = "ubus forward"; 102 | 103 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 104 | continue; 105 | 106 | if (strcmp(type, "forwarding")) 107 | continue; 108 | 109 | forward = fw3_alloc_forward(state); 110 | if (!forward) 111 | continue; 112 | 113 | if (!fw3_parse_blob_options(forward, fw3_forward_opts, entry, name)) 114 | { 115 | warn_section("forward", forward, NULL, "skipped due to invalid options"); 116 | fw3_free_forward(forward); 117 | continue; 118 | } 119 | 120 | if (!check_forward(state, forward, NULL)) 121 | fw3_free_forward(forward); 122 | } 123 | 124 | uci_foreach_element(&p->sections, e) 125 | { 126 | s = uci_to_section(e); 127 | 128 | if (strcmp(s->type, "forwarding")) 129 | continue; 130 | 131 | forward = fw3_alloc_forward(state); 132 | if (!forward) 133 | continue; 134 | 135 | if (!fw3_parse_options(forward, fw3_forward_opts, s)) 136 | warn_elem(e, "has invalid options"); 137 | 138 | if (!check_forward(state, forward, e)) 139 | fw3_free_forward(forward); 140 | } 141 | } 142 | 143 | 144 | static void 145 | append_chain(struct fw3_ipt_rule *r, struct fw3_forward *forward) 146 | { 147 | if (forward->src.any || !forward->src.set) 148 | fw3_ipt_rule_append(r, "FORWARD"); 149 | else 150 | fw3_ipt_rule_append(r, "zone_%s_forward", forward->src.name); 151 | } 152 | 153 | static void set_target(struct fw3_ipt_rule *r, struct fw3_forward *forward) 154 | { 155 | if (forward->dest.any || !forward->dest.set) 156 | fw3_ipt_rule_target(r, "ACCEPT"); 157 | else 158 | fw3_ipt_rule_target(r, "zone_%s_dest_ACCEPT", forward->dest.name); 159 | } 160 | 161 | static void 162 | print_forward(struct fw3_ipt_handle *handle, struct fw3_forward *forward) 163 | { 164 | const char *s, *d; 165 | struct fw3_ipt_rule *r; 166 | 167 | if (handle->table != FW3_TABLE_FILTER) 168 | return; 169 | 170 | if (!fw3_is_family(forward, handle->family)) 171 | return; 172 | 173 | s = forward->_src ? forward->_src->name : "*"; 174 | d = forward->_dest ? forward->_dest->name : "*"; 175 | 176 | info(" * Forward '%s' -> '%s'", s, d); 177 | 178 | if (!fw3_is_family(forward->_src, handle->family) || 179 | !fw3_is_family(forward->_dest, handle->family)) 180 | { 181 | info(" ! Skipping due to different family of zone"); 182 | return; 183 | } 184 | 185 | r = fw3_ipt_rule_new(handle); 186 | fw3_ipt_rule_comment(r, "Zone %s to %s forwarding policy", s, d); 187 | set_target(r, forward); 188 | append_chain(r, forward); 189 | } 190 | 191 | void 192 | fw3_print_forwards(struct fw3_ipt_handle *handle, struct fw3_state *state) 193 | { 194 | struct fw3_forward *forward; 195 | 196 | list_for_each_entry(forward, &state->forwards, list) 197 | print_forward(handle, forward); 198 | } 199 | -------------------------------------------------------------------------------- /includes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "includes.h" 20 | 21 | 22 | const struct fw3_option fw3_include_opts[] = { 23 | FW3_OPT("enabled", bool, include, enabled), 24 | 25 | FW3_OPT("path", string, include, path), 26 | FW3_OPT("type", include_type, include, type), 27 | FW3_OPT("family", family, include, family), 28 | FW3_OPT("reload", bool, include, reload), 29 | 30 | { } 31 | }; 32 | 33 | static bool 34 | check_include(struct fw3_state *state, struct fw3_include *include, struct uci_element *e) 35 | { 36 | if (!include->enabled) 37 | return false; 38 | 39 | if (!include->path) 40 | { 41 | warn_section("include", include, e, "must specify a path"); 42 | return false; 43 | } 44 | 45 | if (include->type == FW3_INC_TYPE_RESTORE && !include->family) 46 | warn_section("include", include, e, "does not specify a family, include will get" 47 | "loaded with both iptables-restore and ip6tables-restore!"); 48 | 49 | return true; 50 | } 51 | 52 | static struct fw3_include * 53 | fw3_alloc_include(struct fw3_state *state) 54 | { 55 | struct fw3_include *include; 56 | 57 | include = calloc(1, sizeof(*include)); 58 | if (!include) 59 | return NULL; 60 | 61 | include->enabled = true; 62 | 63 | list_add_tail(&include->list, &state->includes); 64 | 65 | return include; 66 | } 67 | 68 | void 69 | fw3_load_includes(struct fw3_state *state, struct uci_package *p, 70 | struct blob_attr *a) 71 | { 72 | struct uci_section *s; 73 | struct uci_element *e; 74 | struct fw3_include *include; 75 | struct blob_attr *entry; 76 | unsigned rem; 77 | 78 | INIT_LIST_HEAD(&state->includes); 79 | 80 | blob_for_each_attr(entry, a, rem) 81 | { 82 | const char *type; 83 | const char *name = "ubus include"; 84 | 85 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 86 | continue; 87 | 88 | if (strcmp(type, "script") && strcmp(type, "restore")) 89 | continue; 90 | 91 | include = fw3_alloc_include(state); 92 | if (!include) 93 | continue; 94 | 95 | if (!fw3_parse_blob_options(include, fw3_include_opts, entry, name)) 96 | { 97 | warn_section("include", include, NULL, "skipped due to invalid options"); 98 | fw3_free_include(include); 99 | continue; 100 | } 101 | 102 | if (!check_include(state, include, NULL)) 103 | fw3_free_include(include); 104 | } 105 | 106 | uci_foreach_element(&p->sections, e) 107 | { 108 | s = uci_to_section(e); 109 | 110 | if (strcmp(s->type, "include")) 111 | continue; 112 | 113 | include = fw3_alloc_include(state); 114 | if (!include) 115 | continue; 116 | 117 | include->name = e->name; 118 | 119 | if (!fw3_parse_options(include, fw3_include_opts, s)) 120 | warn_elem(e, "has invalid options"); 121 | 122 | if (!check_include(state, include, e)) 123 | fw3_free_include(include); 124 | } 125 | } 126 | 127 | 128 | static void 129 | print_include(struct fw3_include *include) 130 | { 131 | FILE *f; 132 | char line[1024]; 133 | 134 | info(" * Loading include '%s'", include->path); 135 | 136 | if (!(f = fopen(include->path, "r"))) 137 | { 138 | info(" ! Skipping due to open error: %s", strerror(errno)); 139 | return; 140 | } 141 | 142 | while (fgets(line, sizeof(line), f)) 143 | fw3_pr("%s", line); 144 | 145 | fclose(f); 146 | } 147 | 148 | void 149 | fw3_print_includes(struct fw3_state *state, enum fw3_family family, bool reload) 150 | { 151 | struct fw3_include *include; 152 | 153 | bool exec = false; 154 | const char *restore = "iptables-restore"; 155 | 156 | if (family == FW3_FAMILY_V6) 157 | restore = "ip6tables-restore"; 158 | 159 | list_for_each_entry(include, &state->includes, list) 160 | { 161 | if (reload && !include->reload) 162 | continue; 163 | 164 | if (include->type != FW3_INC_TYPE_RESTORE) 165 | continue; 166 | 167 | if (!fw3_is_family(include, family)) 168 | continue; 169 | 170 | if (!exec) 171 | { 172 | exec = fw3_command_pipe(false, restore, "--noflush"); 173 | 174 | if (!exec) 175 | return; 176 | } 177 | 178 | print_include(include); 179 | } 180 | 181 | if (exec) 182 | fw3_command_close(); 183 | } 184 | 185 | #define TEMPLATE "config() { echo \"You cannot use UCI in firewall includes!\" >&2; exit 1; }; . %s" 186 | 187 | static void 188 | run_include(struct fw3_include *include) 189 | { 190 | int rv; 191 | struct stat s; 192 | char buf[PATH_MAX + sizeof(TEMPLATE)]; 193 | 194 | info(" * Running script '%s'", include->path); 195 | 196 | if (stat(include->path, &s)) 197 | { 198 | info(" ! Skipping due to path error: %s", strerror(errno)); 199 | return; 200 | } 201 | 202 | snprintf(buf, sizeof(buf), TEMPLATE, include->path); 203 | rv = system(buf); 204 | 205 | if (rv) 206 | info(" ! Failed with exit code %u", WEXITSTATUS(rv)); 207 | } 208 | 209 | void 210 | fw3_run_includes(struct fw3_state *state, bool reload) 211 | { 212 | struct fw3_include *include; 213 | 214 | list_for_each_entry(include, &state->includes, list) 215 | { 216 | if (reload && !include->reload) 217 | continue; 218 | 219 | if (include->type == FW3_INC_TYPE_SCRIPT) 220 | run_include(include); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /helpers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2018 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "helpers.h" 20 | 21 | 22 | const struct fw3_option fw3_cthelper_opts[] = { 23 | FW3_OPT("enabled", bool, cthelper, enabled), 24 | FW3_OPT("name", string, cthelper, name), 25 | FW3_OPT("module", string, cthelper, module), 26 | FW3_OPT("description", string, cthelper, description), 27 | FW3_OPT("family", family, cthelper, family), 28 | FW3_LIST("proto", protocol, cthelper, proto), 29 | FW3_OPT("port", port, cthelper, port), 30 | 31 | { } 32 | }; 33 | 34 | 35 | static bool 36 | test_module(struct fw3_cthelper *helper) 37 | { 38 | struct stat s; 39 | char path[sizeof("/sys/module/nf_conntrack_xxxxxxxxxxxxxxxx")]; 40 | 41 | snprintf(path, sizeof(path), "/sys/module/%s", helper->module); 42 | 43 | if (stat(path, &s) || !S_ISDIR(s.st_mode)) 44 | return false; 45 | 46 | return true; 47 | } 48 | 49 | static bool 50 | check_cthelper_proto(const struct fw3_cthelper *helper) 51 | { 52 | struct fw3_protocol *proto; 53 | 54 | if (list_empty(&helper->proto)) 55 | return false; 56 | 57 | list_for_each_entry(proto, &helper->proto, list) 58 | { 59 | if (!proto->protocol || proto->any || proto->invert) 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | static bool 67 | check_cthelper(struct fw3_state *state, struct fw3_cthelper *helper, struct uci_element *e) 68 | { 69 | if (!helper->name || !*helper->name) 70 | { 71 | warn_section("helper", helper, e, "must have a name assigned"); 72 | } 73 | else if (!helper->module || !*helper->module) 74 | { 75 | warn_section("helper", helper, e, "must have a module assigned"); 76 | } 77 | else if (!check_cthelper_proto(helper)) 78 | { 79 | warn_section("helper", helper, e, "must specify a protocol"); 80 | } 81 | else if (helper->port.set && helper->port.invert) 82 | { 83 | warn_section("helper", helper, e, "must not specify negated ports"); 84 | } 85 | else 86 | { 87 | return true; 88 | } 89 | 90 | return false; 91 | } 92 | 93 | static struct fw3_cthelper * 94 | fw3_alloc_cthelper(struct fw3_state *state) 95 | { 96 | struct fw3_cthelper *helper; 97 | 98 | helper = calloc(1, sizeof(*helper)); 99 | if (!helper) 100 | return NULL; 101 | 102 | helper->enabled = true; 103 | helper->family = FW3_FAMILY_ANY; 104 | INIT_LIST_HEAD(&helper->proto); 105 | 106 | list_add_tail(&helper->list, &state->cthelpers); 107 | 108 | return helper; 109 | } 110 | 111 | static void 112 | load_cthelpers(struct fw3_state *state, struct uci_package *p) 113 | { 114 | struct fw3_cthelper *helper; 115 | struct uci_section *s; 116 | struct uci_element *e; 117 | 118 | uci_foreach_element(&p->sections, e) 119 | { 120 | s = uci_to_section(e); 121 | 122 | if (strcmp(s->type, "helper")) 123 | continue; 124 | 125 | helper = fw3_alloc_cthelper(state); 126 | 127 | if (!helper) 128 | continue; 129 | 130 | if (!fw3_parse_options(helper, fw3_cthelper_opts, s)) 131 | warn_elem(e, "has invalid options"); 132 | 133 | if (!check_cthelper(state, helper, e)) 134 | fw3_free_cthelper(helper); 135 | } 136 | } 137 | 138 | void 139 | fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p) 140 | { 141 | struct uci_package *hp = NULL; 142 | FILE *fp; 143 | 144 | INIT_LIST_HEAD(&state->cthelpers); 145 | 146 | fp = fopen(FW3_HELPERCONF, "r"); 147 | 148 | if (fp) { 149 | uci_import(state->uci, fp, "fw3_ct_helpers", &hp, true); 150 | fclose(fp); 151 | 152 | if (hp) 153 | load_cthelpers(state, hp); 154 | } 155 | 156 | load_cthelpers(state, p); 157 | } 158 | 159 | struct fw3_cthelper * 160 | fw3_lookup_cthelper(struct fw3_state *state, const char *name) 161 | { 162 | struct fw3_cthelper *h; 163 | 164 | if (list_empty(&state->cthelpers)) 165 | return NULL; 166 | 167 | list_for_each_entry(h, &state->cthelpers, list) 168 | { 169 | if (strcasecmp(h->name, name)) 170 | continue; 171 | 172 | return h; 173 | } 174 | 175 | return NULL; 176 | } 177 | 178 | bool 179 | fw3_cthelper_check_proto(const struct fw3_cthelper *h, const struct fw3_protocol *proto) 180 | { 181 | struct fw3_protocol *p; 182 | 183 | list_for_each_entry(p, &h->proto, list) 184 | { 185 | if (p->protocol == proto->protocol) 186 | return true; 187 | } 188 | 189 | return false; 190 | } 191 | 192 | struct fw3_cthelper * 193 | fw3_lookup_cthelper_by_proto_port(struct fw3_state *state, 194 | struct fw3_protocol *proto, 195 | struct fw3_port *port) 196 | { 197 | struct fw3_cthelper *h; 198 | 199 | if (list_empty(&state->cthelpers)) 200 | return NULL; 201 | 202 | if (!proto || !proto->protocol || proto->any || proto->invert) 203 | return NULL; 204 | 205 | if (port && port->invert) 206 | return NULL; 207 | 208 | list_for_each_entry(h, &state->cthelpers, list) 209 | { 210 | if (!h->enabled) 211 | continue; 212 | 213 | if (!fw3_cthelper_check_proto(h, proto)) 214 | continue; 215 | 216 | if (h->port.set && (!port || !port->set)) 217 | continue; 218 | 219 | if (!h->port.set && (!port || !port->set)) 220 | return h; 221 | 222 | if (h->port.set && port && port->set && 223 | h->port.port_min <= port->port_min && 224 | h->port.port_max >= port->port_max) 225 | return h; 226 | } 227 | 228 | return NULL; 229 | } 230 | 231 | static void 232 | print_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper, 233 | struct fw3_zone *zone, struct fw3_protocol *proto) 234 | { 235 | struct fw3_ipt_rule *r; 236 | 237 | r = fw3_ipt_rule_create(handle, proto, NULL, NULL, NULL, NULL); 238 | 239 | if (helper->description && *helper->description) 240 | fw3_ipt_rule_comment(r, helper->description); 241 | else 242 | fw3_ipt_rule_comment(r, helper->name); 243 | 244 | fw3_ipt_rule_sport_dport(r, NULL, &helper->port); 245 | fw3_ipt_rule_target(r, "CT"); 246 | fw3_ipt_rule_addarg(r, false, "--helper", helper->name); 247 | fw3_ipt_rule_replace(r, "zone_%s_helper", zone->name); 248 | } 249 | 250 | static void 251 | expand_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper, 252 | struct fw3_zone *zone) 253 | { 254 | struct fw3_protocol *proto; 255 | 256 | list_for_each_entry(proto, &helper->proto, list) 257 | print_helper_rule(handle, helper, zone, proto); 258 | } 259 | 260 | void 261 | fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state, 262 | struct fw3_zone *zone) 263 | { 264 | struct fw3_cthelper *helper; 265 | struct fw3_cthelpermatch *match; 266 | 267 | if (handle->table != FW3_TABLE_RAW) 268 | return; 269 | 270 | if (!fw3_is_family(zone, handle->family)) 271 | return; 272 | 273 | if (list_empty(&zone->cthelpers)) 274 | { 275 | if (zone->masq || !zone->auto_helper) 276 | return; 277 | 278 | if (list_empty(&state->cthelpers)) 279 | return; 280 | 281 | info(" - Using automatic conntrack helper attachment"); 282 | 283 | list_for_each_entry(helper, &state->cthelpers, list) 284 | { 285 | if (!helper || !helper->enabled) 286 | continue; 287 | 288 | if (!fw3_is_family(helper, handle->family)) 289 | continue; 290 | 291 | if (!test_module(helper)) 292 | continue; 293 | 294 | expand_helper_rule(handle, helper, zone); 295 | } 296 | } 297 | else 298 | { 299 | list_for_each_entry(match, &zone->cthelpers, list) 300 | { 301 | helper = match->ptr; 302 | 303 | if (!helper || !helper->enabled) 304 | continue; 305 | 306 | if (!fw3_is_family(helper, handle->family)) 307 | continue; 308 | 309 | if (!test_module(helper)) 310 | { 311 | info(" ! Conntrack module '%s' for helper '%s' is not loaded", 312 | helper->module, helper->name); 313 | continue; 314 | } 315 | 316 | expand_helper_rule(handle, helper, zone); 317 | } 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /ubus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "ubus.h" 20 | 21 | static struct blob_attr *interfaces = NULL; 22 | static struct blob_attr *procd_data; 23 | 24 | 25 | static void dump_cb(struct ubus_request *req, int type, struct blob_attr *msg) 26 | { 27 | static const struct blobmsg_policy policy = { "interface", BLOBMSG_TYPE_ARRAY }; 28 | struct blob_attr *cur; 29 | 30 | blobmsg_parse(&policy, 1, &cur, blob_data(msg), blob_len(msg)); 31 | if (cur) 32 | interfaces = blob_memdup(cur); 33 | } 34 | 35 | static void procd_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) 36 | { 37 | procd_data = blob_memdup(msg); 38 | } 39 | 40 | bool 41 | fw3_ubus_connect(void) 42 | { 43 | bool status = false; 44 | uint32_t id; 45 | struct ubus_context *ctx = ubus_connect(NULL); 46 | struct blob_buf b = { }; 47 | 48 | blob_buf_init(&b, 0); 49 | 50 | if (!ctx) 51 | goto out; 52 | 53 | if (ubus_lookup_id(ctx, "network.interface", &id)) 54 | goto out; 55 | 56 | if (ubus_invoke(ctx, id, "dump", b.head, dump_cb, NULL, 2000)) 57 | goto out; 58 | 59 | status = true; 60 | 61 | if (ubus_lookup_id(ctx, "service", &id)) 62 | goto out; 63 | 64 | blobmsg_add_string(&b, "type", "firewall"); 65 | ubus_invoke(ctx, id, "get_data", b.head, procd_data_cb, NULL, 2000); 66 | 67 | out: 68 | blob_buf_free(&b); 69 | 70 | if (ctx) 71 | ubus_free(ctx); 72 | 73 | return status; 74 | } 75 | 76 | void 77 | fw3_ubus_disconnect(void) 78 | { 79 | free(interfaces); 80 | interfaces = NULL; 81 | } 82 | 83 | static struct fw3_address * 84 | parse_subnet(enum fw3_family family, struct blob_attr *dict, int rem) 85 | { 86 | struct blob_attr *cur; 87 | struct fw3_address *addr; 88 | 89 | addr = calloc(1, sizeof(*addr)); 90 | if (!addr) 91 | return NULL; 92 | 93 | addr->set = true; 94 | addr->family = family; 95 | 96 | __blob_for_each_attr(cur, dict, rem) 97 | { 98 | if (!strcmp(blobmsg_name(cur), "address")) 99 | inet_pton(family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, 100 | blobmsg_get_string(cur), &addr->address.v6); 101 | 102 | else if (!strcmp(blobmsg_name(cur), "mask")) 103 | fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6); 104 | } 105 | 106 | return addr; 107 | } 108 | 109 | static int 110 | parse_subnets(struct list_head *head, enum fw3_family family, 111 | struct blob_attr *list) 112 | { 113 | struct blob_attr *cur; 114 | struct fw3_address *addr; 115 | int rem, n = 0; 116 | 117 | if (!list) 118 | return 0; 119 | 120 | rem = blobmsg_data_len(list); 121 | 122 | __blob_for_each_attr(cur, blobmsg_data(list), rem) 123 | { 124 | addr = parse_subnet(family, blobmsg_data(cur), blobmsg_data_len(cur)); 125 | 126 | if (addr) 127 | { 128 | list_add_tail(&addr->list, head); 129 | n++; 130 | } 131 | } 132 | 133 | return n; 134 | } 135 | 136 | struct fw3_device * 137 | fw3_ubus_device(const char *net) 138 | { 139 | enum { 140 | DEV_INTERFACE, 141 | DEV_DEVICE, 142 | DEV_L3_DEVICE, 143 | __DEV_MAX 144 | }; 145 | static const struct blobmsg_policy policy[__DEV_MAX] = { 146 | [DEV_INTERFACE] = { "interface", BLOBMSG_TYPE_STRING }, 147 | [DEV_DEVICE] = { "device", BLOBMSG_TYPE_STRING }, 148 | [DEV_L3_DEVICE] = { "l3_device", BLOBMSG_TYPE_STRING }, 149 | }; 150 | struct fw3_device *dev = NULL; 151 | struct blob_attr *tb[__DEV_MAX]; 152 | struct blob_attr *cur; 153 | char *name = NULL; 154 | int rem; 155 | 156 | if (!net || !interfaces) 157 | return NULL; 158 | 159 | blobmsg_for_each_attr(cur, interfaces, rem) { 160 | blobmsg_parse(policy, __DEV_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 161 | if (!tb[DEV_INTERFACE] || 162 | strcmp(blobmsg_data(tb[DEV_INTERFACE]), net) != 0) 163 | continue; 164 | 165 | if (tb[DEV_L3_DEVICE]) 166 | name = blobmsg_data(tb[DEV_L3_DEVICE]); 167 | else if (tb[DEV_DEVICE]) 168 | name = blobmsg_data(tb[DEV_DEVICE]); 169 | else 170 | continue; 171 | 172 | break; 173 | } 174 | 175 | if (!name) 176 | return NULL; 177 | 178 | dev = calloc(1, sizeof(*dev)); 179 | 180 | if (!dev) 181 | return NULL; 182 | 183 | snprintf(dev->name, sizeof(dev->name), "%s", name); 184 | dev->set = true; 185 | 186 | return dev; 187 | } 188 | 189 | int 190 | fw3_ubus_address(struct list_head *list, const char *net) 191 | { 192 | enum { 193 | ADDR_INTERFACE, 194 | ADDR_IPV4, 195 | ADDR_IPV6, 196 | ADDR_IPV6_PREFIX, 197 | __ADDR_MAX 198 | }; 199 | static const struct blobmsg_policy policy[__ADDR_MAX] = { 200 | [ADDR_INTERFACE] = { "interface", BLOBMSG_TYPE_STRING }, 201 | [ADDR_IPV4] = { "ipv4-address", BLOBMSG_TYPE_ARRAY }, 202 | [ADDR_IPV6] = { "ipv6-address", BLOBMSG_TYPE_ARRAY }, 203 | [ADDR_IPV6_PREFIX] = { "ipv6-prefix-assignment", BLOBMSG_TYPE_ARRAY }, 204 | }; 205 | struct blob_attr *tb[__ADDR_MAX]; 206 | struct blob_attr *cur; 207 | int rem, n = 0; 208 | 209 | if (!net || !interfaces) 210 | return 0; 211 | 212 | blobmsg_for_each_attr(cur, interfaces, rem) { 213 | blobmsg_parse(policy, __ADDR_MAX, tb, blobmsg_data(cur), blobmsg_len(cur)); 214 | 215 | if (!tb[ADDR_INTERFACE] || 216 | strcmp(blobmsg_data(tb[ADDR_INTERFACE]), net) != 0) 217 | continue; 218 | 219 | n += parse_subnets(list, FW3_FAMILY_V4, tb[ADDR_IPV4]); 220 | n += parse_subnets(list, FW3_FAMILY_V6, tb[ADDR_IPV6]); 221 | n += parse_subnets(list, FW3_FAMILY_V6, tb[ADDR_IPV6_PREFIX]); 222 | } 223 | 224 | return n; 225 | } 226 | 227 | void 228 | fw3_ubus_zone_devices(struct fw3_zone *zone) 229 | { 230 | struct blob_attr *c, *cur, *dcur; 231 | unsigned r, rem, drem; 232 | const char *name; 233 | bool matches; 234 | 235 | blobmsg_for_each_attr(c, interfaces, r) { 236 | name = NULL; 237 | matches = false; 238 | 239 | blobmsg_for_each_attr(cur, c, rem) { 240 | if (!strcmp(blobmsg_name(cur), "interface")) 241 | name = blobmsg_get_string(cur); 242 | else if (!strcmp(blobmsg_name(cur), "data")) 243 | blobmsg_for_each_attr(dcur, cur, drem) 244 | if (!strcmp(blobmsg_name(dcur), "zone")) 245 | matches = !strcmp(blobmsg_get_string(dcur), zone->name); 246 | } 247 | 248 | if (name && matches) 249 | fw3_parse_device(&zone->networks, name, true); 250 | } 251 | } 252 | 253 | static void fw3_ubus_rules_add(struct blob_buf *b, const char *service, 254 | const char *instance, const char *device, 255 | const struct blob_attr *rule, unsigned n) 256 | { 257 | void *k = blobmsg_open_table(b, ""); 258 | struct blob_attr *ropt; 259 | unsigned orem; 260 | char *type = NULL, *name = NULL; 261 | char comment[256]; 262 | 263 | blobmsg_for_each_attr(ropt, rule, orem) { 264 | if (!strcmp(blobmsg_name(ropt), "type")) 265 | type = blobmsg_data(ropt); 266 | else if (!strcmp(blobmsg_name(ropt), "name")) 267 | name = blobmsg_data(ropt); 268 | 269 | if (device && !strcmp(blobmsg_name(ropt), "device")) 270 | device = blobmsg_get_string(ropt); 271 | else if (strcmp(blobmsg_name(ropt), "name")) 272 | blobmsg_add_blob(b, ropt); 273 | } 274 | 275 | if (!type || strcmp(type, "ipset")) { 276 | if (instance) 277 | snprintf(comment, sizeof(comment), "ubus:%s[%s] %s %d", 278 | service, instance, type ? type : "rule", n); 279 | else 280 | snprintf(comment, sizeof(comment), "ubus:%s %s %d", 281 | service, type ? type : "rule", n); 282 | 283 | blobmsg_add_string(b, "name", comment); 284 | } 285 | else if (name) { 286 | blobmsg_add_string(b, "name", name); 287 | } 288 | 289 | if (device) 290 | blobmsg_add_string(b, "device", device); 291 | 292 | blobmsg_close_table(b, k); 293 | } 294 | 295 | void 296 | fw3_ubus_rules(struct blob_buf *b) 297 | { 298 | blob_buf_init(b, 0); 299 | 300 | struct blob_attr *c, *cur, *dcur, *rule; 301 | unsigned n, r, rem, drem, rrem; 302 | 303 | blobmsg_for_each_attr(c, interfaces, r) { 304 | const char *l3_device = NULL; 305 | const char *iface_proto = "unknown"; 306 | const char *iface_name = "unknown"; 307 | struct blob_attr *data = NULL; 308 | 309 | blobmsg_for_each_attr(cur, c, rem) { 310 | if (!strcmp(blobmsg_name(cur), "l3_device")) 311 | l3_device = blobmsg_get_string(cur); 312 | else if (!strcmp(blobmsg_name(cur), "interface")) 313 | iface_name = blobmsg_get_string(cur); 314 | else if (!strcmp(blobmsg_name(cur), "proto")) 315 | iface_proto = blobmsg_get_string(cur); 316 | else if (!strcmp(blobmsg_name(cur), "data")) 317 | data = cur; 318 | } 319 | 320 | if (!data || !l3_device) 321 | continue; 322 | 323 | blobmsg_for_each_attr(dcur, data, drem) { 324 | if (strcmp(blobmsg_name(dcur), "firewall")) 325 | continue; 326 | 327 | n = 0; 328 | 329 | blobmsg_for_each_attr(rule, dcur, rrem) 330 | fw3_ubus_rules_add(b, iface_name, iface_proto, 331 | l3_device, rule, n++); 332 | } 333 | } 334 | 335 | if (!procd_data) 336 | return; 337 | 338 | /* service */ 339 | blobmsg_for_each_attr(c, procd_data, r) { 340 | if (!blobmsg_check_attr(c, true)) 341 | continue; 342 | 343 | /* instance */ 344 | blobmsg_for_each_attr(cur, c, rem) { 345 | if (!blobmsg_check_attr(cur, true)) 346 | continue; 347 | 348 | /* fw rules within the service itself */ 349 | if (!strcmp(blobmsg_name(cur), "firewall")) { 350 | n = 0; 351 | 352 | blobmsg_for_each_attr(rule, cur, rrem) 353 | fw3_ubus_rules_add(b, blobmsg_name(c), 354 | NULL, NULL, rule, n++); 355 | 356 | continue; 357 | } 358 | 359 | /* type */ 360 | blobmsg_for_each_attr(dcur, cur, drem) { 361 | if (!blobmsg_check_attr(dcur, true)) 362 | continue; 363 | 364 | if (strcmp(blobmsg_name(dcur), "firewall")) 365 | continue; 366 | 367 | n = 0; 368 | 369 | blobmsg_for_each_attr(rule, dcur, rrem) 370 | fw3_ubus_rules_add(b, blobmsg_name(c), 371 | blobmsg_name(cur), NULL, rule, n++); 372 | } 373 | } 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /snats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2014 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "snats.h" 20 | 21 | 22 | const struct fw3_option fw3_snat_opts[] = { 23 | FW3_OPT("enabled", bool, snat, enabled), 24 | 25 | FW3_OPT("name", string, snat, name), 26 | FW3_OPT("family", family, snat, family), 27 | 28 | FW3_OPT("src", device, snat, src), 29 | FW3_OPT("device", string, snat, device), 30 | 31 | FW3_OPT("ipset", setmatch, snat, ipset), 32 | 33 | FW3_LIST("proto", protocol, snat, proto), 34 | 35 | FW3_OPT("src_ip", network, snat, ip_src), 36 | FW3_OPT("src_port", port, snat, port_src), 37 | 38 | FW3_OPT("snat_ip", network, snat, ip_snat), 39 | FW3_OPT("snat_port", port, snat, port_snat), 40 | 41 | FW3_OPT("dest_ip", network, snat, ip_dest), 42 | FW3_OPT("dest_port", port, snat, port_dest), 43 | 44 | FW3_OPT("extra", string, snat, extra), 45 | 46 | FW3_OPT("limit", limit, snat, limit), 47 | FW3_OPT("limit_burst", int, snat, limit.burst), 48 | 49 | FW3_OPT("connlimit_ports", bool, snat, connlimit_ports), 50 | 51 | FW3_OPT("utc_time", bool, snat, time.utc), 52 | FW3_OPT("start_date", date, snat, time.datestart), 53 | FW3_OPT("stop_date", date, snat, time.datestop), 54 | FW3_OPT("start_time", time, snat, time.timestart), 55 | FW3_OPT("stop_time", time, snat, time.timestop), 56 | FW3_OPT("weekdays", weekdays, snat, time.weekdays), 57 | FW3_OPT("monthdays", monthdays, snat, time.monthdays), 58 | 59 | FW3_OPT("mark", mark, snat, mark), 60 | 61 | FW3_OPT("target", target, snat, target), 62 | 63 | { } 64 | }; 65 | 66 | 67 | static bool 68 | check_families(struct uci_element *e, struct fw3_snat *r) 69 | { 70 | if (r->family == FW3_FAMILY_ANY) 71 | return true; 72 | 73 | if (r->_src && r->_src->family && r->_src->family != r->family) 74 | { 75 | warn_section("nat", r, e, "refers to source zone with different family"); 76 | return false; 77 | } 78 | 79 | if (r->ipset.ptr && r->ipset.ptr->family && 80 | r->ipset.ptr->family != r->family) 81 | { 82 | warn_section("nat", r, e, "refers to ipset with different family"); 83 | return false; 84 | } 85 | 86 | if (r->ip_src.family && r->ip_src.family != r->family) 87 | { 88 | warn_section("nat", r, e, "uses source ip with different family"); 89 | return false; 90 | } 91 | 92 | if (r->ip_dest.family && r->ip_dest.family != r->family) 93 | { 94 | warn_section("nat", r, e, "uses destination ip with different family"); 95 | return false; 96 | } 97 | 98 | if (r->ip_snat.family && r->ip_snat.family != r->family) 99 | { 100 | warn_section("nat", r, e, "uses snat ip with different family"); 101 | return false; 102 | } 103 | 104 | return true; 105 | } 106 | 107 | 108 | static struct fw3_snat* 109 | alloc_snat(struct fw3_state *state) 110 | { 111 | struct fw3_snat *snat = calloc(1, sizeof(*snat)); 112 | 113 | if (snat) { 114 | INIT_LIST_HEAD(&snat->proto); 115 | list_add_tail(&snat->list, &state->snats); 116 | snat->enabled = true; 117 | } 118 | 119 | return snat; 120 | } 121 | 122 | static bool 123 | check_snat(struct fw3_state *state, struct fw3_snat *snat, struct uci_element *e) 124 | { 125 | if (!snat->enabled) 126 | return false; 127 | 128 | if (snat->src.invert) 129 | { 130 | warn_section("nat", snat, e, "must not have an inverted source"); 131 | return false; 132 | } 133 | else if (snat->src.set && !snat->src.any && 134 | !(snat->_src = fw3_lookup_zone(state, snat->src.name))) 135 | { 136 | warn_section("nat", snat, e, "refers to not existing zone '%s'", snat->src.name); 137 | return false; 138 | } 139 | else if (snat->ipset.set && state->disable_ipsets) 140 | { 141 | warn_section("nat", snat, e, "skipped due to disabled ipset support"); 142 | return false; 143 | } 144 | else if (snat->ipset.set && 145 | !(snat->ipset.ptr = fw3_lookup_ipset(state, snat->ipset.name))) 146 | { 147 | warn_section("nat", snat, e, "refers to unknown ipset '%s'", snat->ipset.name); 148 | return false; 149 | } 150 | 151 | if (!check_families(e, snat)) 152 | return false; 153 | 154 | if (snat->target == FW3_FLAG_UNSPEC) 155 | { 156 | warn_section("nat", snat, e, "has no target specified, defaulting to MASQUERADE"); 157 | snat->target = FW3_FLAG_MASQUERADE; 158 | } 159 | else if (snat->target != FW3_FLAG_ACCEPT && snat->target != FW3_FLAG_SNAT && 160 | snat->target != FW3_FLAG_MASQUERADE) 161 | { 162 | warn_section("nat", snat, e, "has invalid target specified, defaulting to MASQUERADE"); 163 | snat->target = FW3_FLAG_MASQUERADE; 164 | } 165 | 166 | if (snat->target == FW3_FLAG_SNAT && 167 | !snat->ip_snat.set && !snat->port_snat.set) 168 | { 169 | warn_section("nat", snat, e, "needs either 'snat_ip' or 'snat_port' for SNAT"); 170 | return false; 171 | } 172 | else if (snat->target != FW3_FLAG_SNAT && snat->ip_snat.set) 173 | { 174 | warn_section("nat", snat, e, "must not use 'snat_ip' for non-SNAT"); 175 | return false; 176 | } 177 | else if (snat->target != FW3_FLAG_SNAT && snat->port_snat.set) 178 | { 179 | warn_section("nat", snat, e, "must not use 'snat_port' for non-SNAT"); 180 | return false; 181 | } 182 | 183 | if (list_empty(&snat->proto)) 184 | { 185 | warn_section("nat", snat, e, "does not specify a protocol, assuming all"); 186 | fw3_parse_protocol(&snat->proto, "all", true); 187 | } 188 | 189 | if (snat->_src) 190 | set(snat->_src->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT); 191 | 192 | return true; 193 | } 194 | 195 | 196 | void 197 | fw3_load_snats(struct fw3_state *state, struct uci_package *p, struct blob_attr *a) 198 | { 199 | struct uci_section *s; 200 | struct uci_element *e; 201 | struct fw3_snat *snat; 202 | struct blob_attr *entry; 203 | unsigned rem; 204 | 205 | INIT_LIST_HEAD(&state->snats); 206 | 207 | blob_for_each_attr(entry, a, rem) { 208 | const char *type = NULL; 209 | const char *name = "ubus rule"; 210 | 211 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 212 | continue; 213 | 214 | if (strcmp(type, "nat")) 215 | continue; 216 | 217 | snat = alloc_snat(state); 218 | if (!snat) 219 | continue; 220 | 221 | if (!fw3_parse_blob_options(snat, fw3_snat_opts, entry, name)) 222 | { 223 | warn_section("nat", snat, NULL, "skipped due to invalid options"); 224 | fw3_free_snat(snat); 225 | continue; 226 | } 227 | 228 | if (!check_snat(state, snat, NULL)) 229 | fw3_free_snat(snat); 230 | } 231 | 232 | uci_foreach_element(&p->sections, e) 233 | { 234 | s = uci_to_section(e); 235 | 236 | if (strcmp(s->type, "nat")) 237 | continue; 238 | 239 | snat = alloc_snat(state); 240 | if (!snat) 241 | continue; 242 | 243 | if (!fw3_parse_options(snat, fw3_snat_opts, s)) 244 | { 245 | warn_elem(e, "skipped due to invalid options"); 246 | fw3_free_snat(snat); 247 | continue; 248 | } 249 | 250 | if (!check_snat(state, snat, e)) 251 | fw3_free_snat(snat); 252 | } 253 | } 254 | 255 | static void 256 | append_chain(struct fw3_ipt_rule *r, struct fw3_snat *snat) 257 | { 258 | if (snat->_src) 259 | fw3_ipt_rule_append(r, "zone_%s_postrouting", snat->src.name); 260 | else 261 | fw3_ipt_rule_append(r, "POSTROUTING"); 262 | } 263 | 264 | static void 265 | set_target(struct fw3_ipt_rule *r, struct fw3_snat *snat, 266 | struct fw3_protocol *proto) 267 | { 268 | char buf[sizeof("255.255.255.255:65535-65535")] = {}; 269 | char ip[INET_ADDRSTRLEN], portcntbuf[6], *p = buf; 270 | size_t rem = sizeof(buf); 271 | int len; 272 | 273 | if (snat->target == FW3_FLAG_SNAT) 274 | { 275 | if (snat->ip_snat.set) 276 | { 277 | inet_ntop(AF_INET, &snat->ip_snat.address.v4, ip, sizeof(ip)); 278 | 279 | len = snprintf(p, rem, "%s", ip); 280 | 281 | if (len < 0 || len >= rem) 282 | return; 283 | 284 | rem -= len; 285 | p += len; 286 | } 287 | 288 | if (snat->port_snat.set && proto && !proto->any && 289 | (proto->protocol == 6 || proto->protocol == 17 || proto->protocol == 1)) 290 | { 291 | if (snat->port_snat.port_min == snat->port_snat.port_max) 292 | snprintf(p, rem, ":%u", snat->port_snat.port_min); 293 | else 294 | snprintf(p, rem, ":%u-%u", 295 | snat->port_snat.port_min, snat->port_snat.port_max); 296 | 297 | if (snat->connlimit_ports) { 298 | snprintf(portcntbuf, sizeof(portcntbuf), "%u", 299 | 1 + snat->port_snat.port_max - snat->port_snat.port_min); 300 | 301 | fw3_ipt_rule_addarg(r, false, "-m", "connlimit"); 302 | fw3_ipt_rule_addarg(r, false, "--connlimit-daddr", NULL); 303 | fw3_ipt_rule_addarg(r, false, "--connlimit-upto", portcntbuf); 304 | } 305 | } 306 | 307 | fw3_ipt_rule_target(r, "SNAT"); 308 | fw3_ipt_rule_addarg(r, false, "--to-source", buf); 309 | } 310 | else if (snat->target == FW3_FLAG_ACCEPT) 311 | { 312 | fw3_ipt_rule_target(r, "ACCEPT"); 313 | } 314 | else 315 | { 316 | fw3_ipt_rule_target(r, "MASQUERADE"); 317 | } 318 | } 319 | 320 | static void 321 | set_comment(struct fw3_ipt_rule *r, const char *name, int num) 322 | { 323 | if (name) 324 | fw3_ipt_rule_comment(r, name); 325 | else 326 | fw3_ipt_rule_comment(r, "@nat[%u]", num); 327 | } 328 | 329 | static void 330 | print_snat(struct fw3_ipt_handle *h, struct fw3_state *state, 331 | struct fw3_snat *snat, int num, struct fw3_protocol *proto) 332 | { 333 | struct fw3_ipt_rule *r; 334 | struct fw3_address *src, *dst; 335 | struct fw3_port *spt, *dpt; 336 | 337 | switch (h->table) 338 | { 339 | case FW3_TABLE_NAT: 340 | src = &snat->ip_src; 341 | dst = &snat->ip_dest; 342 | spt = &snat->port_src; 343 | dpt = &snat->port_dest; 344 | 345 | r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst); 346 | fw3_ipt_rule_sport_dport(r, spt, dpt); 347 | fw3_ipt_rule_device(r, snat->device, true); 348 | fw3_ipt_rule_ipset(r, &snat->ipset); 349 | fw3_ipt_rule_limit(r, &snat->limit); 350 | fw3_ipt_rule_time(r, &snat->time); 351 | fw3_ipt_rule_mark(r, &snat->mark); 352 | set_target(r, snat, proto); 353 | fw3_ipt_rule_extra(r, snat->extra); 354 | set_comment(r, snat->name, num); 355 | append_chain(r, snat); 356 | break; 357 | 358 | default: 359 | break; 360 | } 361 | } 362 | 363 | static void 364 | expand_snat(struct fw3_ipt_handle *handle, struct fw3_state *state, 365 | struct fw3_snat *snat, int num) 366 | { 367 | struct fw3_protocol *proto; 368 | 369 | if (snat->name) 370 | info(" * NAT '%s'", snat->name); 371 | else 372 | info(" * NAT #%u", num); 373 | 374 | if (!fw3_is_family(snat->_src, handle->family)) 375 | { 376 | info(" ! Skipping due to different family of zone"); 377 | return; 378 | } 379 | 380 | if (!fw3_is_family(&snat->ip_src, handle->family) || 381 | !fw3_is_family(&snat->ip_dest, handle->family) || 382 | !fw3_is_family(&snat->ip_snat, handle->family)) 383 | { 384 | if (!snat->ip_src.resolved || 385 | !snat->ip_dest.resolved || 386 | !snat->ip_snat.resolved) 387 | info(" ! Skipping due to different family of ip address"); 388 | 389 | return; 390 | } 391 | 392 | if (snat->ipset.ptr) 393 | { 394 | if (!fw3_is_family(snat->ipset.ptr, handle->family)) 395 | { 396 | info(" ! Skipping due to different family in ipset"); 397 | return; 398 | } 399 | 400 | if (!fw3_check_ipset(snat->ipset.ptr)) 401 | { 402 | info(" ! Skipping due to missing ipset '%s'", 403 | snat->ipset.ptr->external ? 404 | snat->ipset.ptr->external : snat->ipset.ptr->name); 405 | return; 406 | } 407 | 408 | set(snat->ipset.ptr->flags, handle->family, handle->family); 409 | } 410 | 411 | fw3_foreach(proto, &snat->proto) 412 | print_snat(handle, state, snat, num, proto); 413 | } 414 | 415 | void 416 | fw3_print_snats(struct fw3_ipt_handle *handle, struct fw3_state *state) 417 | { 418 | int num = 0; 419 | struct fw3_snat *snat; 420 | 421 | if (handle->family == FW3_FAMILY_V6) 422 | return; 423 | 424 | if (handle->table != FW3_TABLE_NAT) 425 | return; 426 | 427 | list_for_each_entry(snat, &state->snats, list) 428 | expand_snat(handle, state, snat, num++); 429 | } 430 | -------------------------------------------------------------------------------- /defaults.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "defaults.h" 20 | 21 | 22 | #define C(f, tbl, def, fmt) \ 23 | { FW3_FAMILY_##f, FW3_TABLE_##tbl, FW3_FLAG_##def, fmt } 24 | 25 | static const struct fw3_chain_spec default_chains[] = { 26 | C(ANY, FILTER, UNSPEC, "reject"), 27 | C(ANY, FILTER, CUSTOM_CHAINS, "input_rule"), 28 | C(ANY, FILTER, CUSTOM_CHAINS, "output_rule"), 29 | C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_rule"), 30 | C(ANY, FILTER, SYN_FLOOD, "syn_flood"), 31 | 32 | C(V4, NAT, CUSTOM_CHAINS, "prerouting_rule"), 33 | C(V4, NAT, CUSTOM_CHAINS, "postrouting_rule"), 34 | 35 | { } 36 | }; 37 | 38 | const struct fw3_option fw3_flag_opts[] = { 39 | FW3_OPT("input", target, defaults, policy_input), 40 | FW3_OPT("forward", target, defaults, policy_forward), 41 | FW3_OPT("output", target, defaults, policy_output), 42 | 43 | FW3_OPT("drop_invalid", bool, defaults, drop_invalid), 44 | FW3_OPT("tcp_reject_code", reject_code, defaults, tcp_reject_code), 45 | FW3_OPT("any_reject_code", reject_code, defaults, any_reject_code), 46 | 47 | FW3_OPT("syn_flood", bool, defaults, syn_flood), 48 | FW3_OPT("synflood_protect", bool, defaults, syn_flood), 49 | FW3_OPT("synflood_rate", limit, defaults, syn_flood_rate), 50 | FW3_OPT("synflood_burst", int, defaults, syn_flood_rate.burst), 51 | 52 | FW3_OPT("tcp_syncookies", bool, defaults, tcp_syncookies), 53 | FW3_OPT("tcp_ecn", int, defaults, tcp_ecn), 54 | FW3_OPT("tcp_window_scaling", bool, defaults, tcp_window_scaling), 55 | 56 | FW3_OPT("accept_redirects", bool, defaults, accept_redirects), 57 | FW3_OPT("accept_source_route", bool, defaults, accept_source_route), 58 | 59 | FW3_OPT("auto_helper", bool, defaults, auto_helper), 60 | FW3_OPT("custom_chains", bool, defaults, custom_chains), 61 | FW3_OPT("disable_ipv6", bool, defaults, disable_ipv6), 62 | FW3_OPT("flow_offloading", bool, defaults, flow_offloading), 63 | FW3_OPT("flow_offloading_hw", bool, defaults, flow_offloading_hw), 64 | 65 | FW3_OPT("__flags_v4", int, defaults, flags[0]), 66 | FW3_OPT("__flags_v6", int, defaults, flags[1]), 67 | 68 | { } 69 | }; 70 | 71 | 72 | static void 73 | check_policy(struct uci_element *e, enum fw3_flag *pol, const char *name) 74 | { 75 | if (*pol == FW3_FLAG_UNSPEC) 76 | { 77 | warn_elem(e, "has no %s policy specified, defaulting to DROP", name); 78 | *pol = FW3_FLAG_DROP; 79 | } 80 | else if (*pol > FW3_FLAG_DROP) 81 | { 82 | warn_elem(e, "has invalid %s policy, defaulting to DROP", name); 83 | *pol = FW3_FLAG_DROP; 84 | } 85 | } 86 | 87 | static void 88 | check_target(struct uci_element *e, bool *available, const char *target, const bool ipv6) 89 | { 90 | const bool b = fw3_has_target(ipv6, target); 91 | if (!b) 92 | { 93 | warn_elem(e, "requires unavailable target extension %s, disabling", target); 94 | *available = false; 95 | } 96 | } 97 | 98 | static void 99 | check_any_reject_code(struct uci_element *e, enum fw3_reject_code *any_reject_code) 100 | { 101 | if (*any_reject_code == FW3_REJECT_CODE_TCP_RESET) { 102 | warn_elem(e, "tcp-reset not valid for any_reject_code, defaulting to port-unreach"); 103 | *any_reject_code = FW3_REJECT_CODE_PORT_UNREACH; 104 | } 105 | } 106 | 107 | static const char* 108 | get_reject_code(enum fw3_family family, enum fw3_reject_code reject_code) 109 | { 110 | switch (reject_code) { 111 | case FW3_REJECT_CODE_TCP_RESET: 112 | return "tcp-reset"; 113 | case FW3_REJECT_CODE_PORT_UNREACH: 114 | return "port-unreach"; 115 | case FW3_REJECT_CODE_ADM_PROHIBITED: 116 | return family == FW3_FAMILY_V6 ? "adm-prohibited": "admin-prohib"; 117 | default: 118 | return "unknown"; 119 | } 120 | } 121 | 122 | void 123 | fw3_load_defaults(struct fw3_state *state, struct uci_package *p) 124 | { 125 | struct uci_section *s; 126 | struct uci_element *e; 127 | struct fw3_defaults *defs = &state->defaults; 128 | 129 | bool seen = false; 130 | 131 | defs->tcp_reject_code = FW3_REJECT_CODE_TCP_RESET; 132 | defs->any_reject_code = FW3_REJECT_CODE_PORT_UNREACH; 133 | defs->syn_flood_rate.rate = 25; 134 | defs->syn_flood_rate.burst = 50; 135 | defs->tcp_syncookies = true; 136 | defs->tcp_window_scaling = true; 137 | defs->custom_chains = true; 138 | defs->auto_helper = true; 139 | 140 | uci_foreach_element(&p->sections, e) 141 | { 142 | s = uci_to_section(e); 143 | 144 | if (strcmp(s->type, "defaults")) 145 | continue; 146 | 147 | if (seen) 148 | { 149 | warn_elem(e, "ignoring duplicate section"); 150 | continue; 151 | } 152 | 153 | seen = true; 154 | 155 | if(!fw3_parse_options(&state->defaults, fw3_flag_opts, s)) 156 | warn_elem(e, "has invalid options"); 157 | 158 | check_policy(e, &defs->policy_input, "input"); 159 | check_policy(e, &defs->policy_output, "output"); 160 | check_policy(e, &defs->policy_forward, "forward"); 161 | 162 | check_any_reject_code(e, &defs->any_reject_code); 163 | 164 | /* exists in both ipv4 and ipv6, if at all, so only check ipv4 */ 165 | check_target(e, &defs->flow_offloading, "FLOWOFFLOAD", false); 166 | } 167 | } 168 | 169 | void 170 | fw3_print_default_chains(struct fw3_ipt_handle *handle, struct fw3_state *state, 171 | bool reload) 172 | { 173 | struct fw3_defaults *defs = &state->defaults; 174 | const struct fw3_chain_spec *c; 175 | 176 | #define policy(t) \ 177 | ((t == FW3_FLAG_REJECT) ? FW3_FLAG_DROP : t) 178 | 179 | if (handle->family == FW3_FAMILY_V6 && defs->disable_ipv6) 180 | return; 181 | 182 | if (handle->table == FW3_TABLE_FILTER) 183 | { 184 | fw3_ipt_set_policy(handle, "INPUT", policy(defs->policy_input)); 185 | fw3_ipt_set_policy(handle, "OUTPUT", policy(defs->policy_output)); 186 | fw3_ipt_set_policy(handle, "FORWARD", policy(defs->policy_forward)); 187 | } 188 | 189 | if (defs->custom_chains) 190 | set(defs->flags, handle->family, FW3_FLAG_CUSTOM_CHAINS); 191 | 192 | if (defs->syn_flood) 193 | set(defs->flags, handle->family, FW3_FLAG_SYN_FLOOD); 194 | 195 | for (c = default_chains; c->format; c++) 196 | { 197 | if (!fw3_is_family(c, handle->family)) 198 | continue; 199 | 200 | if (c->table != handle->table) 201 | continue; 202 | 203 | if (c->flag && 204 | !fw3_hasbit(defs->flags[handle->family == FW3_FAMILY_V6], c->flag)) 205 | continue; 206 | 207 | fw3_ipt_create_chain(handle, reload, c->format); 208 | } 209 | 210 | set(defs->flags, handle->family, handle->table); 211 | } 212 | 213 | void 214 | fw3_print_default_head_rules(struct fw3_ipt_handle *handle, 215 | struct fw3_state *state, bool reload) 216 | { 217 | int i; 218 | struct fw3_defaults *defs = &state->defaults; 219 | struct fw3_device lodev = { .set = true, .name = "lo" }; 220 | struct fw3_protocol tcp = { .protocol = 6 }; 221 | struct fw3_ipt_rule *r; 222 | 223 | const char *chains[] = { 224 | "INPUT", "input", 225 | "OUTPUT", "output", 226 | "FORWARD", "forwarding", 227 | }; 228 | 229 | switch (handle->table) 230 | { 231 | case FW3_TABLE_FILTER: 232 | 233 | r = fw3_ipt_rule_create(handle, NULL, &lodev, NULL, NULL, NULL); 234 | fw3_ipt_rule_target(r, "ACCEPT"); 235 | fw3_ipt_rule_append(r, "INPUT"); 236 | 237 | r = fw3_ipt_rule_create(handle, NULL, NULL, &lodev, NULL, NULL); 238 | fw3_ipt_rule_target(r, "ACCEPT"); 239 | fw3_ipt_rule_append(r, "OUTPUT"); 240 | 241 | if (defs->custom_chains) 242 | { 243 | for (i = 0; i < ARRAY_SIZE(chains); i += 2) 244 | { 245 | r = fw3_ipt_rule_new(handle); 246 | fw3_ipt_rule_comment(r, "Custom %s rule chain", chains[i+1]); 247 | fw3_ipt_rule_target(r, "%s_rule", chains[i+1]); 248 | fw3_ipt_rule_append(r, chains[i]); 249 | } 250 | } 251 | 252 | if (defs->flow_offloading) 253 | { 254 | struct fw3_protocol any = {}; 255 | 256 | r = fw3_ipt_rule_new(handle); 257 | fw3_ipt_rule_proto(r, &any); 258 | fw3_ipt_rule_comment(r, "Traffic offloading"); 259 | fw3_ipt_rule_extra(r, "-m conntrack --ctstate RELATED,ESTABLISHED"); 260 | fw3_ipt_rule_target(r, "FLOWOFFLOAD"); 261 | if (defs->flow_offloading_hw) 262 | fw3_ipt_rule_addarg(r, false, "--hw", NULL); 263 | fw3_ipt_rule_append(r, "FORWARD"); 264 | } 265 | 266 | for (i = 0; i < ARRAY_SIZE(chains); i += 2) 267 | { 268 | r = fw3_ipt_rule_new(handle); 269 | fw3_ipt_rule_extra(r, "-m conntrack --ctstate RELATED,ESTABLISHED"); 270 | fw3_ipt_rule_target(r, "ACCEPT"); 271 | fw3_ipt_rule_append(r, chains[i]); 272 | 273 | if (defs->drop_invalid) 274 | { 275 | r = fw3_ipt_rule_new(handle); 276 | fw3_ipt_rule_extra(r, "-m conntrack --ctstate INVALID"); 277 | fw3_ipt_rule_target(r, "DROP"); 278 | fw3_ipt_rule_append(r, chains[i]); 279 | } 280 | } 281 | 282 | if (defs->syn_flood) 283 | { 284 | r = fw3_ipt_rule_create(handle, NULL, NULL, NULL, NULL, NULL); 285 | fw3_ipt_rule_limit(r, &defs->syn_flood_rate); 286 | fw3_ipt_rule_target(r, "RETURN"); 287 | fw3_ipt_rule_append(r, "syn_flood"); 288 | 289 | r = fw3_ipt_rule_new(handle); 290 | fw3_ipt_rule_target(r, "DROP"); 291 | fw3_ipt_rule_append(r, "syn_flood"); 292 | 293 | r = fw3_ipt_rule_create(handle, &tcp, NULL, NULL, NULL, NULL); 294 | fw3_ipt_rule_extra(r, "--syn"); 295 | fw3_ipt_rule_target(r, "syn_flood"); 296 | fw3_ipt_rule_append(r, "INPUT"); 297 | } 298 | 299 | r = fw3_ipt_rule_create(handle, &tcp, NULL, NULL, NULL, NULL); 300 | fw3_ipt_rule_target(r, "REJECT"); 301 | fw3_ipt_rule_addarg(r, false, "--reject-with", get_reject_code(handle->family, defs->tcp_reject_code)); 302 | fw3_ipt_rule_append(r, "reject"); 303 | 304 | r = fw3_ipt_rule_new(handle); 305 | fw3_ipt_rule_target(r, "REJECT"); 306 | fw3_ipt_rule_addarg(r, false, "--reject-with", get_reject_code(handle->family, defs->any_reject_code)); 307 | fw3_ipt_rule_append(r, "reject"); 308 | 309 | break; 310 | 311 | case FW3_TABLE_NAT: 312 | if (defs->custom_chains) 313 | { 314 | r = fw3_ipt_rule_new(handle); 315 | fw3_ipt_rule_comment(r, "Custom prerouting rule chain"); 316 | fw3_ipt_rule_target(r, "prerouting_rule"); 317 | fw3_ipt_rule_append(r, "PREROUTING"); 318 | 319 | r = fw3_ipt_rule_new(handle); 320 | fw3_ipt_rule_comment(r, "Custom postrouting rule chain"); 321 | fw3_ipt_rule_target(r, "postrouting_rule"); 322 | fw3_ipt_rule_append(r, "POSTROUTING"); 323 | } 324 | break; 325 | 326 | default: 327 | break; 328 | } 329 | } 330 | 331 | void 332 | fw3_print_default_tail_rules(struct fw3_ipt_handle *handle, 333 | struct fw3_state *state, bool reload) 334 | { 335 | struct fw3_defaults *defs = &state->defaults; 336 | struct fw3_ipt_rule *r; 337 | 338 | if (handle->table != FW3_TABLE_FILTER) 339 | return; 340 | 341 | if (defs->policy_input == FW3_FLAG_REJECT) 342 | { 343 | r = fw3_ipt_rule_new(handle); 344 | 345 | if (!r) 346 | return; 347 | 348 | fw3_ipt_rule_target(r, "reject"); 349 | fw3_ipt_rule_append(r, "INPUT"); 350 | } 351 | 352 | if (defs->policy_output == FW3_FLAG_REJECT) 353 | { 354 | r = fw3_ipt_rule_new(handle); 355 | 356 | if (!r) 357 | return; 358 | 359 | fw3_ipt_rule_target(r, "reject"); 360 | fw3_ipt_rule_append(r, "OUTPUT"); 361 | } 362 | 363 | if (defs->policy_forward == FW3_FLAG_REJECT) 364 | { 365 | r = fw3_ipt_rule_new(handle); 366 | 367 | if (!r) 368 | return; 369 | 370 | fw3_ipt_rule_target(r, "reject"); 371 | fw3_ipt_rule_append(r, "FORWARD"); 372 | } 373 | } 374 | 375 | static void 376 | set_default(const char *name, int set) 377 | { 378 | FILE *f; 379 | char path[sizeof("/proc/sys/net/ipv4/tcp_window_scaling")]; 380 | 381 | snprintf(path, sizeof(path), "/proc/sys/net/ipv4/tcp_%s", name); 382 | 383 | info(" * Set tcp_%s to %s", name, set ? "on" : "off"); 384 | 385 | if (!(f = fopen(path, "w"))) 386 | { 387 | info(" ! Unable to write value: %s", strerror(errno)); 388 | return; 389 | } 390 | 391 | fprintf(f, "%u\n", set); 392 | fclose(f); 393 | } 394 | 395 | void 396 | fw3_set_defaults(struct fw3_state *state) 397 | { 398 | set_default("ecn", state->defaults.tcp_ecn); 399 | set_default("syncookies", state->defaults.tcp_syncookies); 400 | set_default("window_scaling", state->defaults.tcp_window_scaling); 401 | } 402 | 403 | void 404 | fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, 405 | bool reload) 406 | { 407 | enum fw3_flag policy = reload ? FW3_FLAG_DROP : FW3_FLAG_ACCEPT; 408 | struct fw3_defaults *defs = &state->defaults; 409 | const struct fw3_chain_spec *c; 410 | 411 | if (!has(defs->flags, handle->family, handle->table)) 412 | return; 413 | 414 | if (handle->table == FW3_TABLE_FILTER) 415 | { 416 | fw3_ipt_set_policy(handle, "INPUT", policy); 417 | fw3_ipt_set_policy(handle, "OUTPUT", policy); 418 | fw3_ipt_set_policy(handle, "FORWARD", policy); 419 | } 420 | 421 | fw3_ipt_delete_id_rules(handle, "INPUT"); 422 | fw3_ipt_delete_id_rules(handle, "OUTPUT"); 423 | fw3_ipt_delete_id_rules(handle, "FORWARD"); 424 | fw3_ipt_delete_id_rules(handle, "PREROUTING"); 425 | fw3_ipt_delete_id_rules(handle, "POSTROUTING"); 426 | 427 | /* first flush all the rules ... */ 428 | for (c = default_chains; c->format; c++) 429 | { 430 | /* don't touch user chains on selective stop */ 431 | if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS) 432 | continue; 433 | 434 | if (!fw3_is_family(c, handle->family)) 435 | continue; 436 | 437 | if (c->table != handle->table) 438 | continue; 439 | 440 | if (c->flag && !has(defs->flags, handle->family, c->flag)) 441 | continue; 442 | 443 | fw3_ipt_flush_chain(handle, c->format); 444 | } 445 | 446 | /* ... then remove the chains */ 447 | for (c = default_chains; c->format; c++) 448 | { 449 | if (!fw3_is_family(c, handle->family)) 450 | continue; 451 | 452 | if (c->table != handle->table) 453 | continue; 454 | 455 | if (c->flag && !has(defs->flags, handle->family, c->flag)) 456 | continue; 457 | 458 | fw3_ipt_delete_chain(handle, reload, c->format); 459 | } 460 | 461 | del(defs->flags, handle->family, handle->table); 462 | } 463 | 464 | void 465 | fw3_flush_all(struct fw3_ipt_handle *handle) 466 | { 467 | if (handle->table == FW3_TABLE_FILTER) 468 | { 469 | fw3_ipt_set_policy(handle, "INPUT", FW3_FLAG_ACCEPT); 470 | fw3_ipt_set_policy(handle, "OUTPUT", FW3_FLAG_ACCEPT); 471 | fw3_ipt_set_policy(handle, "FORWARD", FW3_FLAG_ACCEPT); 472 | } 473 | 474 | fw3_ipt_flush(handle); 475 | } 476 | -------------------------------------------------------------------------------- /options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013-2014 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __FW3_OPTIONS_H 20 | #define __FW3_OPTIONS_H 21 | 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #define _LINUX_IN_H 36 | #define _LINUX_IN6_H 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | #include "icmp_codes.h" 49 | #include "utils.h" 50 | 51 | 52 | enum fw3_table 53 | { 54 | FW3_TABLE_FILTER = 0, 55 | FW3_TABLE_NAT = 1, 56 | FW3_TABLE_MANGLE = 2, 57 | FW3_TABLE_RAW = 3, 58 | }; 59 | 60 | enum fw3_family 61 | { 62 | FW3_FAMILY_ANY = 0, 63 | FW3_FAMILY_V4 = 4, 64 | FW3_FAMILY_V6 = 5, 65 | }; 66 | 67 | enum fw3_flag 68 | { 69 | FW3_FLAG_UNSPEC = 0, 70 | FW3_FLAG_ACCEPT = 6, 71 | FW3_FLAG_REJECT = 7, 72 | FW3_FLAG_DROP = 8, 73 | FW3_FLAG_NOTRACK = 9, 74 | FW3_FLAG_HELPER = 10, 75 | FW3_FLAG_MARK = 11, 76 | FW3_FLAG_DSCP = 12, 77 | FW3_FLAG_DNAT = 13, 78 | FW3_FLAG_SNAT = 14, 79 | FW3_FLAG_MASQUERADE = 15, 80 | FW3_FLAG_SRC_ACCEPT = 16, 81 | FW3_FLAG_SRC_REJECT = 17, 82 | FW3_FLAG_SRC_DROP = 18, 83 | FW3_FLAG_CUSTOM_CHAINS = 19, 84 | FW3_FLAG_SYN_FLOOD = 20, 85 | FW3_FLAG_MTU_FIX = 21, 86 | FW3_FLAG_DROP_INVALID = 22, 87 | FW3_FLAG_HOTPLUG = 23, 88 | 89 | __FW3_FLAG_MAX 90 | }; 91 | 92 | enum fw3_reject_code 93 | { 94 | FW3_REJECT_CODE_TCP_RESET = 0, 95 | FW3_REJECT_CODE_PORT_UNREACH = 1, 96 | FW3_REJECT_CODE_ADM_PROHIBITED = 2, 97 | 98 | __FW3_REJECT_CODE_MAX 99 | }; 100 | 101 | extern const char *fw3_flag_names[__FW3_FLAG_MAX]; 102 | 103 | 104 | enum fw3_limit_unit 105 | { 106 | FW3_LIMIT_UNIT_SECOND = 0, 107 | FW3_LIMIT_UNIT_MINUTE = 1, 108 | FW3_LIMIT_UNIT_HOUR = 2, 109 | FW3_LIMIT_UNIT_DAY = 3, 110 | 111 | __FW3_LIMIT_UNIT_MAX 112 | }; 113 | 114 | extern const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX]; 115 | 116 | 117 | enum fw3_ipset_method 118 | { 119 | FW3_IPSET_METHOD_UNSPEC = 0, 120 | FW3_IPSET_METHOD_BITMAP = 1, 121 | FW3_IPSET_METHOD_HASH = 2, 122 | FW3_IPSET_METHOD_LIST = 3, 123 | 124 | __FW3_IPSET_METHOD_MAX 125 | }; 126 | 127 | enum fw3_ipset_type 128 | { 129 | FW3_IPSET_TYPE_UNSPEC = 0, 130 | FW3_IPSET_TYPE_IP = 1, 131 | FW3_IPSET_TYPE_PORT = 2, 132 | FW3_IPSET_TYPE_MAC = 3, 133 | FW3_IPSET_TYPE_NET = 4, 134 | FW3_IPSET_TYPE_SET = 5, 135 | 136 | __FW3_IPSET_TYPE_MAX 137 | }; 138 | 139 | extern const char *fw3_ipset_method_names[__FW3_IPSET_METHOD_MAX]; 140 | extern const char *fw3_ipset_type_names[__FW3_IPSET_TYPE_MAX]; 141 | 142 | 143 | enum fw3_include_type 144 | { 145 | FW3_INC_TYPE_SCRIPT = 0, 146 | FW3_INC_TYPE_RESTORE = 1, 147 | }; 148 | 149 | enum fw3_reflection_source 150 | { 151 | FW3_REFLECTION_INTERNAL = 0, 152 | FW3_REFLECTION_EXTERNAL = 1, 153 | }; 154 | 155 | struct fw3_ipset_datatype 156 | { 157 | struct list_head list; 158 | enum fw3_ipset_type type; 159 | const char *dir; 160 | }; 161 | 162 | struct fw3_setmatch 163 | { 164 | bool set; 165 | bool invert; 166 | char name[32]; 167 | const char *dir[3]; 168 | struct fw3_ipset *ptr; 169 | }; 170 | 171 | struct fw3_device 172 | { 173 | struct list_head list; 174 | 175 | bool set; 176 | bool any; 177 | bool invert; 178 | char name[32]; 179 | char network[32]; 180 | }; 181 | 182 | struct fw3_address 183 | { 184 | struct list_head list; 185 | 186 | bool set; 187 | bool range; 188 | bool invert; 189 | bool resolved; 190 | enum fw3_family family; 191 | union { 192 | struct in_addr v4; 193 | struct in6_addr v6; 194 | struct ether_addr mac; 195 | } address; 196 | union { 197 | struct in_addr v4; 198 | struct in6_addr v6; 199 | struct ether_addr mac; 200 | } mask; 201 | }; 202 | 203 | struct fw3_mac 204 | { 205 | struct list_head list; 206 | 207 | bool set; 208 | bool invert; 209 | struct ether_addr mac; 210 | }; 211 | 212 | struct fw3_protocol 213 | { 214 | struct list_head list; 215 | 216 | bool any; 217 | bool invert; 218 | uint32_t protocol; 219 | }; 220 | 221 | struct fw3_port 222 | { 223 | struct list_head list; 224 | 225 | bool set; 226 | bool invert; 227 | uint16_t port_min; 228 | uint16_t port_max; 229 | }; 230 | 231 | struct fw3_icmptype 232 | { 233 | struct list_head list; 234 | 235 | bool invert; 236 | enum fw3_family family; 237 | uint8_t type; 238 | uint8_t code_min; 239 | uint8_t code_max; 240 | uint8_t type6; 241 | uint8_t code6_min; 242 | uint8_t code6_max; 243 | }; 244 | 245 | struct fw3_limit 246 | { 247 | bool invert; 248 | int rate; 249 | int burst; 250 | enum fw3_limit_unit unit; 251 | }; 252 | 253 | struct fw3_time 254 | { 255 | bool utc; 256 | struct tm datestart; 257 | struct tm datestop; 258 | uint32_t timestart; 259 | uint32_t timestop; 260 | uint32_t monthdays; /* bit 0 is invert + 1 .. 31 */ 261 | uint8_t weekdays; /* bit 0 is invert + 1 .. 7 */ 262 | }; 263 | 264 | struct fw3_mark 265 | { 266 | bool set; 267 | bool invert; 268 | uint32_t mark; 269 | uint32_t mask; 270 | }; 271 | 272 | struct fw3_dscp 273 | { 274 | bool set; 275 | bool invert; 276 | uint8_t dscp; 277 | }; 278 | 279 | struct fw3_cthelpermatch 280 | { 281 | struct list_head list; 282 | 283 | bool set; 284 | bool invert; 285 | char name[32]; 286 | struct fw3_cthelper *ptr; 287 | }; 288 | 289 | struct fw3_defaults 290 | { 291 | enum fw3_flag policy_input; 292 | enum fw3_flag policy_output; 293 | enum fw3_flag policy_forward; 294 | 295 | bool drop_invalid; 296 | enum fw3_reject_code tcp_reject_code; 297 | enum fw3_reject_code any_reject_code; 298 | 299 | bool syn_flood; 300 | struct fw3_limit syn_flood_rate; 301 | 302 | bool tcp_syncookies; 303 | int tcp_ecn; 304 | bool tcp_window_scaling; 305 | 306 | bool accept_redirects; 307 | bool accept_source_route; 308 | 309 | bool custom_chains; 310 | bool auto_helper; 311 | bool flow_offloading; 312 | bool flow_offloading_hw; 313 | 314 | bool disable_ipv6; 315 | 316 | uint32_t flags[2]; 317 | }; 318 | 319 | struct fw3_zone 320 | { 321 | struct list_head list; 322 | 323 | bool enabled; 324 | const char *name; 325 | 326 | enum fw3_family family; 327 | 328 | enum fw3_flag policy_input; 329 | enum fw3_flag policy_output; 330 | enum fw3_flag policy_forward; 331 | 332 | struct list_head networks; 333 | struct list_head devices; 334 | struct list_head subnets; 335 | 336 | const char *extra_src; 337 | const char *extra_dest; 338 | 339 | bool masq; 340 | bool masq_allow_invalid; 341 | struct list_head masq_src; 342 | struct list_head masq_dest; 343 | 344 | bool mtu_fix; 345 | 346 | struct list_head cthelpers; 347 | 348 | int log; 349 | struct fw3_limit log_limit; 350 | 351 | bool custom_chains; 352 | bool auto_helper; 353 | 354 | uint32_t flags[2]; 355 | 356 | struct list_head old_addrs; 357 | }; 358 | 359 | struct fw3_rule 360 | { 361 | struct list_head list; 362 | 363 | bool enabled; 364 | const char *name; 365 | 366 | enum fw3_family family; 367 | 368 | struct fw3_zone *_src; 369 | struct fw3_zone *_dest; 370 | 371 | const char *device; 372 | bool direction_out; 373 | 374 | struct fw3_device src; 375 | struct fw3_device dest; 376 | struct fw3_setmatch ipset; 377 | struct fw3_cthelpermatch helper; 378 | 379 | struct list_head proto; 380 | 381 | struct list_head ip_src; 382 | struct list_head mac_src; 383 | struct list_head port_src; 384 | 385 | struct list_head ip_dest; 386 | struct list_head port_dest; 387 | 388 | struct list_head icmp_type; 389 | 390 | struct fw3_limit limit; 391 | struct fw3_time time; 392 | struct fw3_mark mark; 393 | struct fw3_dscp dscp; 394 | 395 | enum fw3_flag target; 396 | struct fw3_mark set_mark; 397 | struct fw3_mark set_xmark; 398 | struct fw3_dscp set_dscp; 399 | struct fw3_cthelpermatch set_helper; 400 | 401 | const char *extra; 402 | }; 403 | 404 | struct fw3_redirect 405 | { 406 | struct list_head list; 407 | 408 | bool enabled; 409 | const char *name; 410 | 411 | enum fw3_family family; 412 | 413 | struct fw3_zone *_src; 414 | struct fw3_zone *_dest; 415 | 416 | struct fw3_device src; 417 | struct fw3_device dest; 418 | struct fw3_setmatch ipset; 419 | struct fw3_cthelpermatch helper; 420 | 421 | struct list_head proto; 422 | 423 | struct fw3_address ip_src; 424 | struct list_head mac_src; 425 | struct fw3_port port_src; 426 | 427 | struct fw3_address ip_dest; 428 | struct fw3_port port_dest; 429 | 430 | struct fw3_address ip_redir; 431 | struct fw3_port port_redir; 432 | 433 | struct fw3_limit limit; 434 | struct fw3_time time; 435 | struct fw3_mark mark; 436 | 437 | enum fw3_flag target; 438 | 439 | const char *extra; 440 | 441 | bool local; 442 | bool reflection; 443 | enum fw3_reflection_source reflection_src; 444 | struct list_head reflection_zones; 445 | }; 446 | 447 | struct fw3_snat 448 | { 449 | struct list_head list; 450 | 451 | bool enabled; 452 | const char *name; 453 | 454 | enum fw3_family family; 455 | 456 | struct fw3_zone *_src; 457 | 458 | struct fw3_device src; 459 | struct fw3_setmatch ipset; 460 | struct fw3_cthelpermatch helper; 461 | const char *device; 462 | 463 | struct list_head proto; 464 | 465 | struct fw3_address ip_src; 466 | struct fw3_port port_src; 467 | 468 | struct fw3_address ip_dest; 469 | struct fw3_port port_dest; 470 | 471 | struct fw3_address ip_snat; 472 | struct fw3_port port_snat; 473 | 474 | struct fw3_limit limit; 475 | struct fw3_time time; 476 | struct fw3_mark mark; 477 | bool connlimit_ports; 478 | 479 | enum fw3_flag target; 480 | 481 | const char *extra; 482 | }; 483 | 484 | struct fw3_forward 485 | { 486 | struct list_head list; 487 | 488 | bool enabled; 489 | const char *name; 490 | 491 | enum fw3_family family; 492 | 493 | struct fw3_zone *_src; 494 | struct fw3_zone *_dest; 495 | 496 | struct fw3_device src; 497 | struct fw3_device dest; 498 | }; 499 | 500 | struct fw3_ipset 501 | { 502 | struct list_head list; 503 | 504 | bool enabled; 505 | bool reload_set; 506 | bool counters; 507 | bool comment; 508 | 509 | const char *name; 510 | enum fw3_family family; 511 | 512 | enum fw3_ipset_method method; 513 | struct list_head datatypes; 514 | 515 | struct fw3_address iprange; 516 | struct fw3_port portrange; 517 | 518 | int netmask; 519 | int maxelem; 520 | int hashsize; 521 | 522 | int timeout; 523 | 524 | const char *external; 525 | 526 | struct list_head entries; 527 | const char *loadfile; 528 | 529 | uint32_t flags[2]; 530 | }; 531 | 532 | struct fw3_include 533 | { 534 | struct list_head list; 535 | 536 | bool enabled; 537 | const char *name; 538 | enum fw3_family family; 539 | 540 | const char *path; 541 | enum fw3_include_type type; 542 | 543 | bool reload; 544 | }; 545 | 546 | struct fw3_cthelper 547 | { 548 | struct list_head list; 549 | 550 | bool enabled; 551 | const char *name; 552 | const char *module; 553 | const char *description; 554 | enum fw3_family family; 555 | struct list_head proto; 556 | struct fw3_port port; 557 | }; 558 | 559 | struct fw3_setentry 560 | { 561 | struct list_head list; 562 | const char *value; 563 | }; 564 | 565 | struct fw3_state 566 | { 567 | struct uci_context *uci; 568 | struct fw3_defaults defaults; 569 | struct list_head zones; 570 | struct list_head rules; 571 | struct list_head redirects; 572 | struct list_head snats; 573 | struct list_head forwards; 574 | struct list_head ipsets; 575 | struct list_head includes; 576 | struct list_head cthelpers; 577 | 578 | bool disable_ipsets; 579 | bool statefile; 580 | }; 581 | 582 | struct fw3_chain_spec { 583 | int family; 584 | int table; 585 | int flag; 586 | const char *format; 587 | }; 588 | 589 | 590 | struct fw3_option 591 | { 592 | const char *name; 593 | bool (*parse)(void *, const char *, bool); 594 | uintptr_t offset; 595 | size_t elem_size; 596 | }; 597 | 598 | #define FW3_OPT(name, parse, structure, member) \ 599 | { name, fw3_parse_##parse, offsetof(struct fw3_##structure, member) } 600 | 601 | #define FW3_LIST(name, parse, structure, member) \ 602 | { name, fw3_parse_##parse, offsetof(struct fw3_##structure, member), \ 603 | sizeof(struct fw3_##structure) } 604 | 605 | bool fw3_parse_bool(void *ptr, const char *val, bool is_list); 606 | bool fw3_parse_int(void *ptr, const char *val, bool is_list); 607 | bool fw3_parse_string(void *ptr, const char *val, bool is_list); 608 | bool fw3_parse_target(void *ptr, const char *val, bool is_list); 609 | bool fw3_parse_reject_code(void *ptr, const char *val, bool is_list); 610 | bool fw3_parse_limit(void *ptr, const char *val, bool is_list); 611 | bool fw3_parse_device(void *ptr, const char *val, bool is_list); 612 | bool fw3_parse_address(void *ptr, const char *val, bool is_list); 613 | bool fw3_parse_network(void *ptr, const char *val, bool is_list); 614 | bool fw3_parse_mac(void *ptr, const char *val, bool is_list); 615 | bool fw3_parse_port(void *ptr, const char *val, bool is_list); 616 | bool fw3_parse_family(void *ptr, const char *val, bool is_list); 617 | bool fw3_parse_icmptype(void *ptr, const char *val, bool is_list); 618 | bool fw3_parse_protocol(void *ptr, const char *val, bool is_list); 619 | 620 | bool fw3_parse_ipset_method(void *ptr, const char *val, bool is_list); 621 | bool fw3_parse_ipset_datatype(void *ptr, const char *val, bool is_list); 622 | 623 | bool fw3_parse_include_type(void *ptr, const char *val, bool is_list); 624 | bool fw3_parse_reflection_source(void *ptr, const char *val, bool is_list); 625 | 626 | bool fw3_parse_date(void *ptr, const char *val, bool is_list); 627 | bool fw3_parse_time(void *ptr, const char *val, bool is_list); 628 | bool fw3_parse_weekdays(void *ptr, const char *val, bool is_list); 629 | bool fw3_parse_monthdays(void *ptr, const char *val, bool is_list); 630 | bool fw3_parse_mark(void *ptr, const char *val, bool is_list); 631 | bool fw3_parse_dscp(void *ptr, const char *val, bool is_list); 632 | bool fw3_parse_setmatch(void *ptr, const char *val, bool is_list); 633 | bool fw3_parse_direction(void *ptr, const char *val, bool is_list); 634 | bool fw3_parse_cthelper(void *ptr, const char *val, bool is_list); 635 | bool fw3_parse_setentry(void *ptr, const char *val, bool is_list); 636 | 637 | bool fw3_parse_options(void *s, const struct fw3_option *opts, 638 | struct uci_section *section); 639 | bool fw3_parse_blob_options(void *s, const struct fw3_option *opts, 640 | struct blob_attr *a, const char *name); 641 | 642 | const char * fw3_address_to_string(struct fw3_address *address, 643 | bool allow_invert, bool as_cidr); 644 | 645 | #endif 646 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013-2014 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "options.h" 23 | #include "defaults.h" 24 | #include "zones.h" 25 | #include "rules.h" 26 | #include "redirects.h" 27 | #include "snats.h" 28 | #include "forwards.h" 29 | #include "ipsets.h" 30 | #include "includes.h" 31 | #include "ubus.h" 32 | #include "iptables.h" 33 | #include "helpers.h" 34 | 35 | 36 | static enum fw3_family print_family = FW3_FAMILY_ANY; 37 | 38 | static struct fw3_state *run_state = NULL; 39 | static struct fw3_state *cfg_state = NULL; 40 | 41 | 42 | static bool 43 | build_state(bool runtime) 44 | { 45 | struct fw3_state *state = NULL; 46 | struct uci_package *p = NULL; 47 | FILE *sf; 48 | 49 | state = calloc(1, sizeof(*state)); 50 | if (!state) 51 | error("Out of memory"); 52 | 53 | state->uci = uci_alloc_context(); 54 | 55 | if (!state->uci) 56 | error("Out of memory"); 57 | 58 | if (runtime) 59 | { 60 | sf = fopen(FW3_STATEFILE, "r"); 61 | 62 | if (sf) 63 | { 64 | uci_import(state->uci, sf, "fw3_state", &p, true); 65 | fclose(sf); 66 | } 67 | 68 | if (!p) 69 | { 70 | uci_free_context(state->uci); 71 | free(state); 72 | 73 | return false; 74 | } 75 | 76 | state->statefile = true; 77 | 78 | run_state = state; 79 | } 80 | else 81 | { 82 | if (!fw3_ubus_connect()) 83 | warn("Failed to connect to ubus"); 84 | 85 | if (uci_load(state->uci, "firewall", &p)) 86 | { 87 | uci_perror(state->uci, NULL); 88 | error("Failed to load /etc/config/firewall"); 89 | } 90 | 91 | if (!fw3_find_command("ipset")) 92 | { 93 | warn("Unable to locate ipset utility, disabling ipset support"); 94 | state->disable_ipsets = true; 95 | } 96 | 97 | cfg_state = state; 98 | } 99 | 100 | 101 | struct blob_buf b = {NULL, NULL, 0, NULL}; 102 | fw3_ubus_rules(&b); 103 | 104 | fw3_load_defaults(state, p); 105 | fw3_load_cthelpers(state, p); 106 | fw3_load_ipsets(state, p, b.head); 107 | fw3_load_zones(state, p); 108 | fw3_load_rules(state, p, b.head); 109 | fw3_load_redirects(state, p, b.head); 110 | fw3_load_snats(state, p, b.head); 111 | fw3_load_forwards(state, p, b.head); 112 | fw3_load_includes(state, p, b.head); 113 | 114 | return true; 115 | } 116 | 117 | static void 118 | free_state(struct fw3_state *state) 119 | { 120 | struct list_head *cur, *tmp; 121 | 122 | list_for_each_safe(cur, tmp, &state->zones) 123 | fw3_free_zone((struct fw3_zone *)cur); 124 | 125 | list_for_each_safe(cur, tmp, &state->rules) 126 | fw3_free_rule((struct fw3_rule *)cur); 127 | 128 | list_for_each_safe(cur, tmp, &state->redirects) 129 | fw3_free_redirect((struct fw3_redirect *)cur); 130 | 131 | list_for_each_safe(cur, tmp, &state->snats) 132 | fw3_free_snat((struct fw3_snat *)cur); 133 | 134 | list_for_each_safe(cur, tmp, &state->forwards) 135 | fw3_free_forward((struct fw3_forward *)cur); 136 | 137 | list_for_each_safe(cur, tmp, &state->ipsets) 138 | fw3_free_ipset((struct fw3_ipset *)cur); 139 | 140 | list_for_each_safe(cur, tmp, &state->includes) 141 | fw3_free_include((struct fw3_include *)cur); 142 | 143 | list_for_each_safe(cur, tmp, &state->cthelpers) 144 | fw3_free_cthelper((struct fw3_cthelper *)cur); 145 | 146 | uci_free_context(state->uci); 147 | 148 | free(state); 149 | 150 | fw3_ubus_disconnect(); 151 | } 152 | 153 | 154 | static bool 155 | family_running(enum fw3_family family) 156 | { 157 | return (run_state && has(run_state->defaults.flags, family, family)); 158 | } 159 | 160 | static void 161 | family_set(struct fw3_state *state, enum fw3_family family, bool set) 162 | { 163 | if (!state) 164 | return; 165 | 166 | if (set) 167 | set(state->defaults.flags, family, family); 168 | else 169 | del(state->defaults.flags, family, family); 170 | } 171 | 172 | static int 173 | stop(bool complete) 174 | { 175 | int rv = 1; 176 | enum fw3_family family; 177 | enum fw3_table table; 178 | struct fw3_ipt_handle *handle; 179 | 180 | if (!complete && !run_state) 181 | { 182 | warn("The firewall appears to be stopped. " 183 | "Use the 'flush' command to forcefully purge all rules."); 184 | 185 | return rv; 186 | } 187 | 188 | if (!print_family && run_state) 189 | fw3_hotplug_zones(run_state, false); 190 | 191 | for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) 192 | { 193 | if (!complete && !family_running(family)) 194 | continue; 195 | 196 | for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) 197 | { 198 | if (!(handle = fw3_ipt_open(family, table))) 199 | continue; 200 | 201 | info(" * %sing %s %s table", complete ? "Flush" : "Clear", 202 | fw3_flag_names[family], fw3_flag_names[table]); 203 | 204 | if (complete) 205 | { 206 | fw3_flush_all(handle); 207 | } 208 | else if (run_state) 209 | { 210 | fw3_flush_rules(handle, run_state, false); 211 | fw3_flush_zones(handle, run_state, false); 212 | } 213 | 214 | fw3_ipt_commit(handle); 215 | fw3_ipt_close(handle); 216 | } 217 | 218 | family_set(run_state, family, false); 219 | family_set(cfg_state, family, false); 220 | 221 | rv = 0; 222 | } 223 | 224 | if (run_state) { 225 | for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) 226 | fw3_destroy_ipsets(run_state, family, false); 227 | } 228 | 229 | if (complete) 230 | fw3_flush_conntrack(NULL); 231 | 232 | if (!rv && run_state) 233 | fw3_write_statefile(run_state); 234 | 235 | return rv; 236 | } 237 | 238 | static int 239 | start(void) 240 | { 241 | int rv = 1; 242 | enum fw3_family family; 243 | enum fw3_table table; 244 | struct fw3_ipt_handle *handle; 245 | 246 | for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) 247 | { 248 | if (!print_family) 249 | fw3_create_ipsets(cfg_state, family, false); 250 | 251 | if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6) 252 | continue; 253 | 254 | if (print_family && family != print_family) 255 | continue; 256 | 257 | if (!print_family && family_running(family)) 258 | { 259 | warn("The %s firewall appears to be started already. " 260 | "If it is indeed empty, remove the %s file and retry.", 261 | fw3_flag_names[family], FW3_STATEFILE); 262 | 263 | continue; 264 | } 265 | 266 | for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) 267 | { 268 | if (!(handle = fw3_ipt_open(family, table))) 269 | continue; 270 | 271 | info(" * Populating %s %s table", 272 | fw3_flag_names[family], fw3_flag_names[table]); 273 | 274 | fw3_print_default_chains(handle, cfg_state, false); 275 | fw3_print_zone_chains(handle, cfg_state, false); 276 | fw3_print_default_head_rules(handle, cfg_state, false); 277 | fw3_print_rules(handle, cfg_state); 278 | fw3_print_redirects(handle, cfg_state); 279 | fw3_print_snats(handle, cfg_state); 280 | fw3_print_forwards(handle, cfg_state); 281 | fw3_print_zone_rules(handle, cfg_state, false); 282 | fw3_print_default_tail_rules(handle, cfg_state, false); 283 | 284 | if (!print_family) 285 | fw3_ipt_commit(handle); 286 | 287 | fw3_ipt_close(handle); 288 | } 289 | 290 | if (!print_family) 291 | fw3_print_includes(cfg_state, family, false); 292 | 293 | family_set(run_state, family, true); 294 | family_set(cfg_state, family, true); 295 | 296 | rv = 0; 297 | } 298 | 299 | if (!rv) 300 | { 301 | fw3_flush_conntrack(run_state); 302 | fw3_set_defaults(cfg_state); 303 | 304 | if (!print_family) 305 | { 306 | fw3_run_includes(cfg_state, false); 307 | fw3_hotplug_zones(cfg_state, true); 308 | fw3_write_statefile(cfg_state); 309 | } 310 | } 311 | 312 | return rv; 313 | } 314 | 315 | 316 | static int 317 | reload(void) 318 | { 319 | int rv = 1; 320 | enum fw3_family family; 321 | enum fw3_table table; 322 | struct fw3_ipt_handle *handle; 323 | 324 | if (!run_state) 325 | return start(); 326 | 327 | fw3_hotplug_zones(run_state, false); 328 | 329 | for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) 330 | { 331 | if (!family_running(family)) 332 | goto start; 333 | 334 | for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) 335 | { 336 | if (!(handle = fw3_ipt_open(family, table))) 337 | continue; 338 | 339 | info(" * Clearing %s %s table", 340 | fw3_flag_names[family], fw3_flag_names[table]); 341 | 342 | fw3_flush_rules(handle, run_state, true); 343 | fw3_flush_zones(handle, run_state, true); 344 | fw3_ipt_commit(handle); 345 | fw3_ipt_close(handle); 346 | } 347 | 348 | fw3_ipsets_update_run_state(family, run_state, cfg_state); 349 | fw3_destroy_ipsets(run_state, family, true); 350 | 351 | family_set(run_state, family, false); 352 | family_set(cfg_state, family, false); 353 | 354 | start: 355 | if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6) 356 | continue; 357 | 358 | fw3_create_ipsets(cfg_state, family, true); 359 | 360 | for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) 361 | { 362 | if (!(handle = fw3_ipt_open(family, table))) 363 | continue; 364 | 365 | info(" * Populating %s %s table", 366 | fw3_flag_names[family], fw3_flag_names[table]); 367 | 368 | fw3_print_default_chains(handle, cfg_state, true); 369 | fw3_print_zone_chains(handle, cfg_state, true); 370 | fw3_print_default_head_rules(handle, cfg_state, true); 371 | fw3_print_rules(handle, cfg_state); 372 | fw3_print_redirects(handle, cfg_state); 373 | fw3_print_snats(handle, cfg_state); 374 | fw3_print_forwards(handle, cfg_state); 375 | fw3_print_zone_rules(handle, cfg_state, true); 376 | fw3_print_default_tail_rules(handle, cfg_state, true); 377 | 378 | fw3_ipt_commit(handle); 379 | fw3_ipt_close(handle); 380 | } 381 | 382 | fw3_print_includes(cfg_state, family, true); 383 | 384 | family_set(run_state, family, true); 385 | family_set(cfg_state, family, true); 386 | 387 | rv = 0; 388 | } 389 | 390 | if (!rv) 391 | { 392 | fw3_flush_conntrack(run_state); 393 | 394 | fw3_set_defaults(cfg_state); 395 | fw3_run_includes(cfg_state, true); 396 | fw3_hotplug_zones(cfg_state, true); 397 | fw3_write_statefile(cfg_state); 398 | } 399 | 400 | return rv; 401 | } 402 | 403 | static int 404 | gc(void) 405 | { 406 | enum fw3_family family; 407 | enum fw3_table table; 408 | struct fw3_ipt_handle *handle; 409 | 410 | for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) 411 | { 412 | if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6) 413 | continue; 414 | 415 | for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) 416 | { 417 | if (!(handle = fw3_ipt_open(family, table))) 418 | continue; 419 | 420 | fw3_ipt_gc(handle); 421 | fw3_ipt_commit(handle); 422 | fw3_ipt_close(handle); 423 | } 424 | } 425 | 426 | return 0; 427 | } 428 | 429 | static int 430 | lookup_network(const char *net) 431 | { 432 | struct fw3_zone *z; 433 | struct fw3_device *d; 434 | 435 | list_for_each_entry(z, &cfg_state->zones, list) 436 | { 437 | list_for_each_entry(d, &z->networks, list) 438 | { 439 | if (!strcmp(d->name, net)) 440 | { 441 | printf("%s\n", z->name); 442 | return 0; 443 | } 444 | } 445 | } 446 | 447 | return 1; 448 | } 449 | 450 | static int 451 | lookup_device(const char *dev) 452 | { 453 | struct fw3_zone *z; 454 | struct fw3_device *d; 455 | 456 | list_for_each_entry(z, &cfg_state->zones, list) 457 | { 458 | list_for_each_entry(d, &z->devices, list) 459 | { 460 | if (!strcmp(d->name, dev)) 461 | { 462 | printf("%s\n", z->name); 463 | return 0; 464 | } 465 | } 466 | } 467 | 468 | return 1; 469 | } 470 | 471 | static int 472 | lookup_zone(const char *zone, const char *device) 473 | { 474 | struct fw3_zone *z; 475 | struct fw3_device *d; 476 | 477 | list_for_each_entry(z, &cfg_state->zones, list) 478 | { 479 | if (strcmp(z->name, zone)) 480 | continue; 481 | 482 | list_for_each_entry(d, &z->devices, list) 483 | { 484 | if (device && strcmp(device, d->name)) 485 | continue; 486 | 487 | printf("%s\n", d->name); 488 | 489 | if (device) 490 | return 0; 491 | } 492 | 493 | if (!device) 494 | return 0; 495 | } 496 | 497 | return 1; 498 | } 499 | 500 | static int 501 | usage(void) 502 | { 503 | fprintf(stderr, "fw3 [-4] [-6] [-q] print\n"); 504 | fprintf(stderr, "fw3 [-q] {start|stop|flush|reload|restart}\n"); 505 | fprintf(stderr, "fw3 [-q] network {net}\n"); 506 | fprintf(stderr, "fw3 [-q] device {dev}\n"); 507 | fprintf(stderr, "fw3 [-q] zone {zone} [dev]\n"); 508 | 509 | return 1; 510 | } 511 | 512 | 513 | int main(int argc, char **argv) 514 | { 515 | int ch, rv = 1; 516 | enum fw3_family family = FW3_FAMILY_ANY; 517 | struct fw3_defaults *defs = NULL; 518 | 519 | while ((ch = getopt(argc, argv, "46dqh")) != -1) 520 | { 521 | switch (ch) 522 | { 523 | case '4': 524 | family = FW3_FAMILY_V4; 525 | break; 526 | 527 | case '6': 528 | family = FW3_FAMILY_V6; 529 | break; 530 | 531 | case 'd': 532 | fw3_pr_debug = true; 533 | break; 534 | 535 | case 'q': 536 | if (freopen("/dev/null", "w", stderr)) {} 537 | break; 538 | 539 | case 'h': 540 | rv = usage(); 541 | goto out; 542 | } 543 | } 544 | 545 | build_state(false); 546 | defs = &cfg_state->defaults; 547 | 548 | if (optind >= argc) 549 | { 550 | rv = usage(); 551 | goto out; 552 | } 553 | 554 | if (!strcmp(argv[optind], "print")) 555 | { 556 | if (family == FW3_FAMILY_ANY) 557 | { 558 | family = FW3_FAMILY_V4; 559 | } 560 | else if (family == FW3_FAMILY_V6) 561 | { 562 | if (defs->disable_ipv6) 563 | warn("IPv6 rules globally disabled in configuration"); 564 | #ifdef DISABLE_IPV6 565 | else 566 | warn("IPv6 support is not compiled in"); 567 | #endif 568 | } 569 | 570 | if (freopen("/dev/null", "w", stderr)) {}; 571 | 572 | cfg_state->disable_ipsets = true; 573 | print_family = family; 574 | fw3_pr_debug = true; 575 | 576 | if (fw3_lock()) 577 | { 578 | build_state(true); 579 | rv = start(); 580 | fw3_unlock(); 581 | } 582 | } 583 | else if (!strcmp(argv[optind], "start")) 584 | { 585 | if (fw3_lock()) 586 | { 587 | build_state(true); 588 | rv = start(); 589 | fw3_unlock(); 590 | } 591 | } 592 | else if (!strcmp(argv[optind], "stop")) 593 | { 594 | if (fw3_lock()) 595 | { 596 | build_state(true); 597 | rv = stop(false); 598 | fw3_unlock(); 599 | } 600 | } 601 | else if (!strcmp(argv[optind], "flush")) 602 | { 603 | if (fw3_lock()) 604 | { 605 | build_state(true); 606 | rv = stop(true); 607 | fw3_unlock(); 608 | } 609 | } 610 | else if (!strcmp(argv[optind], "restart")) 611 | { 612 | if (fw3_lock()) 613 | { 614 | build_state(true); 615 | stop(true); 616 | rv = start(); 617 | fw3_unlock(); 618 | } 619 | } 620 | else if (!strcmp(argv[optind], "reload")) 621 | { 622 | if (fw3_lock()) 623 | { 624 | build_state(true); 625 | rv = reload(); 626 | fw3_unlock(); 627 | } 628 | } 629 | else if (!strcmp(argv[optind], "gc")) 630 | { 631 | if (fw3_lock()) 632 | { 633 | rv = gc(); 634 | fw3_unlock(); 635 | } 636 | } 637 | else if (!strcmp(argv[optind], "network") && (optind + 1) < argc) 638 | { 639 | rv = lookup_network(argv[optind + 1]); 640 | } 641 | else if (!strcmp(argv[optind], "device") && (optind + 1) < argc) 642 | { 643 | rv = lookup_device(argv[optind + 1]); 644 | } 645 | else if (!strcmp(argv[optind], "zone") && (optind + 1) < argc) 646 | { 647 | rv = lookup_zone(argv[optind + 1], argv[optind + 2]); 648 | } 649 | else 650 | { 651 | rv = usage(); 652 | } 653 | 654 | out: 655 | if (cfg_state) 656 | free_state(cfg_state); 657 | 658 | if (run_state) 659 | free_state(run_state); 660 | 661 | return rv; 662 | } 663 | -------------------------------------------------------------------------------- /ipsets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | #include "ipsets.h" 22 | 23 | 24 | const struct fw3_option fw3_ipset_opts[] = { 25 | FW3_OPT("enabled", bool, ipset, enabled), 26 | FW3_OPT("reload_set", bool, ipset, reload_set), 27 | FW3_OPT("counters", bool, ipset, counters), 28 | FW3_OPT("comment", bool, ipset, comment), 29 | 30 | FW3_OPT("name", string, ipset, name), 31 | FW3_OPT("family", family, ipset, family), 32 | 33 | FW3_OPT("storage", ipset_method, ipset, method), 34 | FW3_LIST("match", ipset_datatype, ipset, datatypes), 35 | 36 | FW3_OPT("iprange", address, ipset, iprange), 37 | FW3_OPT("portrange", port, ipset, portrange), 38 | 39 | FW3_OPT("netmask", int, ipset, netmask), 40 | FW3_OPT("maxelem", int, ipset, maxelem), 41 | FW3_OPT("hashsize", int, ipset, hashsize), 42 | FW3_OPT("timeout", int, ipset, timeout), 43 | 44 | FW3_OPT("external", string, ipset, external), 45 | 46 | FW3_LIST("entry", setentry, ipset, entries), 47 | FW3_OPT("loadfile", string, ipset, loadfile), 48 | 49 | { } 50 | }; 51 | 52 | #define T(m, t1, t2, t3, r, o) \ 53 | { FW3_IPSET_METHOD_##m, \ 54 | FW3_IPSET_TYPE_##t1 | (FW3_IPSET_TYPE_##t2 << 8) | (FW3_IPSET_TYPE_##t3 << 16), \ 55 | r, o } 56 | 57 | enum ipset_optflag { 58 | OPT_IPRANGE = (1 << 0), 59 | OPT_PORTRANGE = (1 << 1), 60 | OPT_NETMASK = (1 << 2), 61 | OPT_HASHSIZE = (1 << 3), 62 | OPT_MAXELEM = (1 << 4), 63 | OPT_FAMILY = (1 << 5), 64 | }; 65 | 66 | struct ipset_type { 67 | enum fw3_ipset_method method; 68 | uint32_t types; 69 | uint8_t required; 70 | uint8_t optional; 71 | }; 72 | 73 | static struct ipset_type ipset_types[] = { 74 | T(BITMAP, IP, UNSPEC, UNSPEC, OPT_IPRANGE, OPT_NETMASK), 75 | T(BITMAP, IP, MAC, UNSPEC, OPT_IPRANGE, 0), 76 | T(BITMAP, PORT, UNSPEC, UNSPEC, OPT_PORTRANGE, 0), 77 | 78 | T(HASH, IP, UNSPEC, UNSPEC, 0, 79 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM | OPT_NETMASK), 80 | T(HASH, NET, UNSPEC, UNSPEC, 0, 81 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 82 | T(HASH, IP, PORT, UNSPEC, 0, 83 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 84 | T(HASH, NET, PORT, UNSPEC, 0, 85 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 86 | T(HASH, IP, PORT, IP, 0, 87 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 88 | T(HASH, IP, PORT, NET, 0, 89 | OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 90 | 91 | T(LIST, SET, UNSPEC, UNSPEC, 0, OPT_MAXELEM), 92 | }; 93 | 94 | 95 | static bool 96 | check_types(struct uci_element *e, struct fw3_ipset *ipset) 97 | { 98 | int i = 0; 99 | uint32_t typelist = 0; 100 | struct fw3_ipset_datatype *type; 101 | 102 | list_for_each_entry(type, &ipset->datatypes, list) 103 | { 104 | if (i >= 3) 105 | { 106 | warn_section("ipset", ipset, e, "must not have more than 3 datatypes assigned"); 107 | return false; 108 | } 109 | 110 | typelist |= (type->type << (i++ * 8)); 111 | } 112 | 113 | /* find a suitable storage method if none specified */ 114 | if (ipset->method == FW3_IPSET_METHOD_UNSPEC) 115 | { 116 | for (i = 0; i < ARRAY_SIZE(ipset_types); i++) 117 | { 118 | /* skip type for v6 if it does not support family */ 119 | if (ipset->family != FW3_FAMILY_V4 && 120 | !(ipset_types[i].optional & OPT_FAMILY)) 121 | continue; 122 | 123 | if (ipset_types[i].types == typelist) 124 | { 125 | ipset->method = ipset_types[i].method; 126 | 127 | warn_section("ipset", ipset, e, "defines no storage method, assuming '%s'", 128 | fw3_ipset_method_names[ipset->method]); 129 | 130 | break; 131 | } 132 | } 133 | } 134 | 135 | //typelist |= ipset->method; 136 | 137 | for (i = 0; i < ARRAY_SIZE(ipset_types); i++) 138 | { 139 | if (ipset_types[i].method == ipset->method && 140 | ipset_types[i].types == typelist) 141 | { 142 | if (!ipset->external) 143 | { 144 | if ((ipset_types[i].required & OPT_IPRANGE) && 145 | !ipset->iprange.set) 146 | { 147 | warn_section("ipset", ipset, e, "requires an ip range"); 148 | return false; 149 | } 150 | 151 | if ((ipset_types[i].required & OPT_PORTRANGE) && 152 | !ipset->portrange.set) 153 | { 154 | warn_section("ipset", ipset, e, "requires a port range"); 155 | return false; 156 | } 157 | 158 | if (!(ipset_types[i].required & OPT_IPRANGE) && 159 | ipset->iprange.set) 160 | { 161 | warn_section("ipset", ipset, e, "iprange ignored"); 162 | ipset->iprange.set = false; 163 | } 164 | 165 | if (!(ipset_types[i].required & OPT_PORTRANGE) && 166 | ipset->portrange.set) 167 | { 168 | warn_section("ipset", ipset, e, "portrange ignored"); 169 | ipset->portrange.set = false; 170 | } 171 | 172 | if (!(ipset_types[i].optional & OPT_NETMASK) && 173 | ipset->netmask > 0) 174 | { 175 | warn_section("ipset", ipset, e, "netmask ignored"); 176 | ipset->netmask = 0; 177 | } 178 | 179 | if (!(ipset_types[i].optional & OPT_HASHSIZE) && 180 | ipset->hashsize > 0) 181 | { 182 | warn_section("ipset", ipset, e, "hashsize ignored"); 183 | ipset->hashsize = 0; 184 | } 185 | 186 | if (!(ipset_types[i].optional & OPT_MAXELEM) && 187 | ipset->maxelem > 0) 188 | { 189 | warn_section("ipset", ipset, e, "maxelem ignored"); 190 | ipset->maxelem = 0; 191 | } 192 | 193 | if (!(ipset_types[i].optional & OPT_FAMILY) && 194 | ipset->family != FW3_FAMILY_V4) 195 | { 196 | warn_section("ipset", ipset, e, "family ignored"); 197 | ipset->family = FW3_FAMILY_V4; 198 | } 199 | } 200 | 201 | return true; 202 | } 203 | } 204 | 205 | warn_section("ipset", ipset, e, "has an invalid combination of storage method and matches"); 206 | return false; 207 | } 208 | 209 | static bool 210 | check_ipset(struct fw3_state *state, struct fw3_ipset *ipset, struct uci_element *e) 211 | { 212 | if (!ipset->enabled) { 213 | return false; 214 | } 215 | 216 | if (ipset->external) 217 | { 218 | if (!*ipset->external) 219 | ipset->external = NULL; 220 | else if (!ipset->name) 221 | ipset->name = ipset->external; 222 | } 223 | 224 | if (!ipset->name || !*ipset->name) 225 | { 226 | warn_section("ipset", ipset, e, "ipset must have a name assigned"); 227 | } 228 | //else if (fw3_lookup_ipset(state, ipset->name) != NULL) 229 | //{ 230 | // warn_section("ipset", ipset, e, "has duplicated set name", ipset->name); 231 | //} 232 | else if (ipset->family == FW3_FAMILY_ANY) 233 | { 234 | warn_section("ipset", ipset, e, "must not have family 'any'"); 235 | } 236 | else if (ipset->iprange.set && ipset->family != ipset->iprange.family) 237 | { 238 | warn_section("ipset", ipset, e, "has iprange of wrong address family"); 239 | } 240 | else if (list_empty(&ipset->datatypes)) 241 | { 242 | warn_section("ipset", ipset, e, "has no datatypes assigned"); 243 | } 244 | else if (check_types(e, ipset)) 245 | { 246 | return true; 247 | } 248 | 249 | return false; 250 | } 251 | 252 | static struct fw3_ipset * 253 | fw3_alloc_ipset(struct fw3_state *state) 254 | { 255 | struct fw3_ipset *ipset; 256 | 257 | ipset = calloc(1, sizeof(*ipset)); 258 | if (!ipset) 259 | return NULL; 260 | 261 | INIT_LIST_HEAD(&ipset->datatypes); 262 | INIT_LIST_HEAD(&ipset->entries); 263 | 264 | ipset->comment = false; 265 | ipset->counters = false; 266 | ipset->enabled = true; 267 | ipset->family = FW3_FAMILY_V4; 268 | ipset->reload_set = false; 269 | ipset->timeout = -1; /* no timeout by default */ 270 | 271 | list_add_tail(&ipset->list, &state->ipsets); 272 | 273 | return ipset; 274 | } 275 | 276 | void 277 | fw3_load_ipsets(struct fw3_state *state, struct uci_package *p, 278 | struct blob_attr *a) 279 | { 280 | struct uci_section *s; 281 | struct uci_element *e; 282 | struct fw3_ipset *ipset; 283 | struct blob_attr *entry; 284 | unsigned rem; 285 | 286 | INIT_LIST_HEAD(&state->ipsets); 287 | 288 | if (state->disable_ipsets) 289 | return; 290 | 291 | blob_for_each_attr(entry, a, rem) 292 | { 293 | const char *type; 294 | const char *name = "ubus ipset"; 295 | 296 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 297 | continue; 298 | 299 | if (strcmp(type, "ipset")) 300 | continue; 301 | 302 | ipset = fw3_alloc_ipset(state); 303 | if (!ipset) 304 | continue; 305 | 306 | if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name)) 307 | { 308 | warn_section("ipset", ipset, NULL, "skipped due to invalid options"); 309 | fw3_free_ipset(ipset); 310 | continue; 311 | } 312 | 313 | if (!check_ipset(state, ipset, NULL)) 314 | fw3_free_ipset(ipset); 315 | } 316 | 317 | uci_foreach_element(&p->sections, e) 318 | { 319 | s = uci_to_section(e); 320 | 321 | if (strcmp(s->type, "ipset")) 322 | continue; 323 | 324 | ipset = fw3_alloc_ipset(state); 325 | 326 | if (!ipset) 327 | continue; 328 | 329 | if (!fw3_parse_options(ipset, fw3_ipset_opts, s)) 330 | warn_elem(e, "has invalid options"); 331 | 332 | if (!check_ipset(state, ipset, e)) 333 | fw3_free_ipset(ipset); 334 | } 335 | } 336 | 337 | 338 | static void 339 | load_file(struct fw3_ipset *ipset) 340 | { 341 | FILE *f; 342 | char line[128]; 343 | char *p; 344 | 345 | if (!ipset->loadfile) 346 | return; 347 | 348 | info(" * Loading file %s", ipset->loadfile); 349 | 350 | f = fopen(ipset->loadfile, "r"); 351 | 352 | if (!f) { 353 | info(" ! Skipping due to open error: %s", strerror(errno)); 354 | return; 355 | } 356 | 357 | while (fgets(line, sizeof(line), f)) { 358 | p = line; 359 | while (isspace(*p)) 360 | p++; 361 | if (*p && *p != '#') 362 | fw3_pr("add %s %s", ipset->name, line); 363 | } 364 | 365 | fclose(f); 366 | } 367 | 368 | static void 369 | create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) 370 | { 371 | bool first = true; 372 | struct fw3_setentry *entry; 373 | struct fw3_ipset_datatype *type; 374 | 375 | info(" * Creating ipset %s", ipset->name); 376 | 377 | first = true; 378 | fw3_pr("create %s %s", ipset->name, fw3_ipset_method_names[ipset->method]); 379 | 380 | list_for_each_entry(type, &ipset->datatypes, list) 381 | { 382 | fw3_pr("%c%s", first ? ':' : ',', fw3_ipset_type_names[type->type]); 383 | first = false; 384 | } 385 | 386 | if (ipset->method == FW3_IPSET_METHOD_HASH) 387 | fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6"); 388 | 389 | if (ipset->iprange.set) 390 | { 391 | fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true)); 392 | } 393 | else if (ipset->portrange.set) 394 | { 395 | fw3_pr(" range %u-%u", 396 | ipset->portrange.port_min, ipset->portrange.port_max); 397 | } 398 | 399 | if (ipset->timeout >= 0) 400 | fw3_pr(" timeout %u", ipset->timeout); 401 | 402 | if (ipset->maxelem > 0) 403 | fw3_pr(" maxelem %u", ipset->maxelem); 404 | 405 | if (ipset->netmask > 0) 406 | fw3_pr(" netmask %u", ipset->netmask); 407 | 408 | if (ipset->hashsize > 0) 409 | fw3_pr(" hashsize %u", ipset->hashsize); 410 | 411 | if (ipset->counters) 412 | fw3_pr(" counters"); 413 | 414 | if (ipset->comment) 415 | fw3_pr(" comment"); 416 | 417 | fw3_pr("\n"); 418 | 419 | list_for_each_entry(entry, &ipset->entries, list) 420 | fw3_pr("add %s %s\n", ipset->name, entry->value); 421 | 422 | load_file(ipset); 423 | } 424 | 425 | void 426 | fw3_create_ipsets(struct fw3_state *state, enum fw3_family family, 427 | bool reload_set) 428 | { 429 | unsigned int delay, tries; 430 | bool exec = false; 431 | struct fw3_ipset *ipset; 432 | 433 | if (state->disable_ipsets) 434 | return; 435 | 436 | /* spawn ipsets */ 437 | list_for_each_entry(ipset, &state->ipsets, list) 438 | { 439 | if (ipset->family != family) 440 | continue; 441 | 442 | if (ipset->external) 443 | continue; 444 | 445 | if (fw3_check_ipset(ipset) && 446 | (reload_set && !ipset->reload_set)) 447 | continue; 448 | 449 | if (!exec) 450 | { 451 | exec = fw3_command_pipe(false, "ipset", "-exist", "-"); 452 | 453 | if (!exec) 454 | return; 455 | } 456 | 457 | create_ipset(ipset, state); 458 | } 459 | 460 | if (exec) 461 | { 462 | fw3_pr("quit\n"); 463 | fw3_command_close(); 464 | } 465 | 466 | /* wait a little expontially for ipsets to appear */ 467 | list_for_each_entry(ipset, &state->ipsets, list) 468 | { 469 | if (ipset->external) 470 | continue; 471 | 472 | delay = 5; 473 | for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++) 474 | usleep(delay<<1); 475 | } 476 | } 477 | 478 | void 479 | fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family, 480 | bool reload_set) 481 | { 482 | unsigned int delay, tries; 483 | bool exec = false; 484 | struct fw3_ipset *ipset; 485 | 486 | if (state->disable_ipsets) 487 | return; 488 | 489 | /* destroy ipsets */ 490 | list_for_each_entry(ipset, &state->ipsets, list) 491 | { 492 | if (ipset->family != family || 493 | (reload_set && !ipset->reload_set)) 494 | continue; 495 | 496 | if (!exec) 497 | { 498 | exec = fw3_command_pipe(false, "ipset", "-exist", "-"); 499 | 500 | if (!exec) 501 | return; 502 | } 503 | 504 | info(" * Deleting ipset %s", ipset->name); 505 | 506 | fw3_pr("flush %s\n", ipset->name); 507 | fw3_pr("destroy %s\n", ipset->name); 508 | } 509 | 510 | if (exec) 511 | { 512 | fw3_pr("quit\n"); 513 | fw3_command_close(); 514 | } 515 | 516 | /* wait for ipsets to disappear */ 517 | list_for_each_entry(ipset, &state->ipsets, list) 518 | { 519 | if (ipset->external) 520 | continue; 521 | 522 | delay = 5; 523 | for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++) 524 | usleep(delay<<1); 525 | } 526 | } 527 | 528 | struct fw3_ipset * 529 | fw3_lookup_ipset(struct fw3_state *state, const char *name) 530 | { 531 | struct fw3_ipset *s; 532 | 533 | if (list_empty(&state->ipsets)) 534 | return NULL; 535 | 536 | list_for_each_entry(s, &state->ipsets, list) 537 | { 538 | if (strcmp(s->name, name)) 539 | continue; 540 | 541 | return s; 542 | } 543 | 544 | return NULL; 545 | } 546 | 547 | bool 548 | fw3_check_ipset(struct fw3_ipset *set) 549 | { 550 | bool rv = false; 551 | 552 | socklen_t sz; 553 | int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 554 | struct ip_set_req_version req_ver; 555 | struct ip_set_req_get_set req_name; 556 | 557 | if (s < 0 || fcntl(s, F_SETFD, FD_CLOEXEC)) 558 | goto out; 559 | 560 | sz = sizeof(req_ver); 561 | req_ver.op = IP_SET_OP_VERSION; 562 | 563 | if (getsockopt(s, SOL_IP, SO_IP_SET, &req_ver, &sz)) 564 | goto out; 565 | 566 | sz = sizeof(req_name); 567 | req_name.op = IP_SET_OP_GET_BYNAME; 568 | req_name.version = req_ver.version; 569 | snprintf(req_name.set.name, IPSET_MAXNAMELEN - 1, "%s", 570 | set->external ? set->external : set->name); 571 | 572 | if (getsockopt(s, SOL_IP, SO_IP_SET, &req_name, &sz)) 573 | goto out; 574 | 575 | rv = ((sz == sizeof(req_name)) && (req_name.set.index != IPSET_INVALID_ID)); 576 | 577 | out: 578 | if (s >= 0) 579 | close(s); 580 | 581 | return rv; 582 | } 583 | 584 | void 585 | fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state, 586 | struct fw3_state *cfg_state) 587 | { 588 | struct fw3_ipset *ipset_run, *ipset_cfg; 589 | bool in_cfg; 590 | 591 | list_for_each_entry(ipset_run, &run_state->ipsets, list) { 592 | if (ipset_run->family != family) 593 | continue; 594 | 595 | in_cfg = false; 596 | 597 | list_for_each_entry(ipset_cfg, &cfg_state->ipsets, list) { 598 | if (ipset_cfg->family != family) 599 | continue; 600 | 601 | if (strlen(ipset_run->name) == 602 | strlen(ipset_cfg->name) && 603 | !strcmp(ipset_run->name, ipset_cfg->name)) { 604 | in_cfg = true; 605 | break; 606 | } 607 | } 608 | 609 | /* If a set is found in run_state, but not in cfg_state then the 610 | * set has been deleted/renamed. Set reload_set to true to force 611 | * the old set to be destroyed in the "stop" fase of the reload. 612 | * If the set is found, then copy the reload_set value from the 613 | * configuration state. This ensures that the elements are 614 | * always updated according to the configuration, and not the 615 | * runtime state (which the user might have forgotten). 616 | */ 617 | if (!in_cfg) 618 | ipset_run->reload_set = true; 619 | else 620 | ipset_run->reload_set = ipset_cfg->reload_set; 621 | } 622 | } 623 | -------------------------------------------------------------------------------- /rules.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013-2018 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "rules.h" 20 | 21 | 22 | const struct fw3_option fw3_rule_opts[] = { 23 | FW3_OPT("enabled", bool, rule, enabled), 24 | 25 | FW3_OPT("name", string, rule, name), 26 | FW3_OPT("family", family, rule, family), 27 | 28 | FW3_OPT("src", device, rule, src), 29 | FW3_OPT("dest", device, rule, dest), 30 | 31 | FW3_OPT("device", string, rule, device), 32 | FW3_OPT("direction", direction, rule, direction_out), 33 | 34 | FW3_OPT("ipset", setmatch, rule, ipset), 35 | FW3_OPT("helper", cthelper, rule, helper), 36 | FW3_OPT("set_helper", cthelper, rule, set_helper), 37 | 38 | FW3_LIST("proto", protocol, rule, proto), 39 | 40 | FW3_LIST("src_ip", network, rule, ip_src), 41 | FW3_LIST("src_mac", mac, rule, mac_src), 42 | FW3_LIST("src_port", port, rule, port_src), 43 | 44 | FW3_LIST("dest_ip", network, rule, ip_dest), 45 | FW3_LIST("dest_port", port, rule, port_dest), 46 | 47 | FW3_LIST("icmp_type", icmptype, rule, icmp_type), 48 | FW3_OPT("extra", string, rule, extra), 49 | 50 | FW3_OPT("limit", limit, rule, limit), 51 | FW3_OPT("limit_burst", int, rule, limit.burst), 52 | 53 | FW3_OPT("utc_time", bool, rule, time.utc), 54 | FW3_OPT("start_date", date, rule, time.datestart), 55 | FW3_OPT("stop_date", date, rule, time.datestop), 56 | FW3_OPT("start_time", time, rule, time.timestart), 57 | FW3_OPT("stop_time", time, rule, time.timestop), 58 | FW3_OPT("weekdays", weekdays, rule, time.weekdays), 59 | FW3_OPT("monthdays", monthdays, rule, time.monthdays), 60 | 61 | FW3_OPT("mark", mark, rule, mark), 62 | FW3_OPT("set_mark", mark, rule, set_mark), 63 | FW3_OPT("set_xmark", mark, rule, set_xmark), 64 | 65 | FW3_OPT("dscp", dscp, rule, dscp), 66 | FW3_OPT("set_dscp", dscp, rule, set_dscp), 67 | 68 | FW3_OPT("target", target, rule, target), 69 | 70 | { } 71 | }; 72 | 73 | 74 | static bool 75 | need_src_action_chain(struct fw3_rule *r) 76 | { 77 | return (r->_src && r->_src->log && (r->target > FW3_FLAG_ACCEPT)); 78 | } 79 | 80 | static struct fw3_rule* 81 | alloc_rule(struct fw3_state *state) 82 | { 83 | struct fw3_rule *rule = calloc(1, sizeof(*rule)); 84 | 85 | if (rule) { 86 | INIT_LIST_HEAD(&rule->proto); 87 | 88 | INIT_LIST_HEAD(&rule->ip_src); 89 | INIT_LIST_HEAD(&rule->mac_src); 90 | INIT_LIST_HEAD(&rule->port_src); 91 | 92 | INIT_LIST_HEAD(&rule->ip_dest); 93 | INIT_LIST_HEAD(&rule->port_dest); 94 | 95 | INIT_LIST_HEAD(&rule->icmp_type); 96 | 97 | list_add_tail(&rule->list, &state->rules); 98 | rule->enabled = true; 99 | } 100 | 101 | return rule; 102 | } 103 | 104 | static bool 105 | check_rule(struct fw3_state *state, struct fw3_rule *r, struct uci_element *e) 106 | { 107 | if (!r->enabled) 108 | return false; 109 | 110 | if (r->src.invert || r->dest.invert) 111 | { 112 | warn_section("rule", r, e, "must not have inverted 'src' or 'dest' options"); 113 | return false; 114 | } 115 | else if (r->src.set && !r->src.any && 116 | !(r->_src = fw3_lookup_zone(state, r->src.name))) 117 | { 118 | warn_section("rule", r, e, "refers to not existing zone '%s'", r->src.name); 119 | return false; 120 | } 121 | else if (r->dest.set && !r->dest.any && 122 | !(r->_dest = fw3_lookup_zone(state, r->dest.name))) 123 | { 124 | warn_section("rule", r, e, "refers to not existing zone '%s'", r->dest.name); 125 | return false; 126 | } 127 | else if (r->ipset.set && state->disable_ipsets) 128 | { 129 | warn_section("rule", r, e, "skipped due to disabled ipset support"); 130 | return false; 131 | } 132 | else if (r->ipset.set && 133 | !(r->ipset.ptr = fw3_lookup_ipset(state, r->ipset.name))) 134 | { 135 | warn_section("rule", r, e, "refers to unknown ipset '%s'", r->ipset.name); 136 | return false; 137 | } 138 | else if (r->helper.set && 139 | !(r->helper.ptr = fw3_lookup_cthelper(state, r->helper.name))) 140 | { 141 | warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->helper.name); 142 | return false; 143 | } 144 | else if (r->set_helper.set && 145 | !(r->set_helper.ptr = fw3_lookup_cthelper(state, r->set_helper.name))) 146 | { 147 | warn_section("rule", r, e, "refers to unknown CT helper '%s'", r->set_helper.name); 148 | return false; 149 | } 150 | 151 | if (!r->_src && (r->target == FW3_FLAG_NOTRACK || r->target == FW3_FLAG_HELPER)) 152 | { 153 | warn_section("rule", r, e, "is set to target %s but has no source assigned", 154 | fw3_flag_names[r->target]); 155 | return false; 156 | } 157 | 158 | if (!r->set_mark.set && !r->set_xmark.set && 159 | r->target == FW3_FLAG_MARK) 160 | { 161 | warn_section("rule", r, e, "is set to target MARK but specifies neither " 162 | "'set_mark' nor 'set_xmark' option"); 163 | return false; 164 | } 165 | 166 | if (!r->set_dscp.set && r->target == FW3_FLAG_DSCP) 167 | { 168 | warn_section("rule", r, e, "is set to target DSCP but specifies no " 169 | "'set_dscp' option"); 170 | return false; 171 | } 172 | 173 | if (r->set_mark.invert || r->set_xmark.invert || r->set_dscp.invert) 174 | { 175 | warn_section("rule", r, e, "must not have inverted 'set_mark', " 176 | "'set_xmark' or 'set_dscp'"); 177 | return false; 178 | } 179 | 180 | if (!r->set_helper.set && r->target == FW3_FLAG_HELPER) 181 | { 182 | warn_section("rule", r, e, "is set to target HELPER but specifies " 183 | "no 'set_helper' option"); 184 | return false; 185 | } 186 | 187 | if (r->set_helper.invert && r->target == FW3_FLAG_HELPER) 188 | { 189 | warn_section("rule", r, e, "must not have inverted 'set_helper' option"); 190 | return false; 191 | } 192 | 193 | if (!r->_src && !r->_dest && !r->src.any && !r->dest.any) 194 | { 195 | warn_section("rule", r, e, "has neither a source nor a destination zone assigned " 196 | "- assuming an output rule"); 197 | } 198 | 199 | if (list_empty(&r->proto)) 200 | { 201 | warn_section("rule", r, e, "does not specify a protocol, assuming TCP+UDP"); 202 | fw3_parse_protocol(&r->proto, "tcpudp", true); 203 | } 204 | 205 | if (r->target == FW3_FLAG_UNSPEC) 206 | { 207 | warn_section("rule", r, e, "has no target specified, defaulting to REJECT"); 208 | r->target = FW3_FLAG_REJECT; 209 | } 210 | else if (r->target > FW3_FLAG_DSCP) 211 | { 212 | warn_section("rule", r, e, "has invalid target specified, defaulting to REJECT"); 213 | r->target = FW3_FLAG_REJECT; 214 | } 215 | 216 | /* NB: r family... */ 217 | if (r->_dest) 218 | { 219 | fw3_setbit(r->_dest->flags[0], r->target); 220 | fw3_setbit(r->_dest->flags[1], r->target); 221 | } 222 | else if (need_src_action_chain(r)) 223 | { 224 | fw3_setbit(r->_src->flags[0], fw3_to_src_target(r->target)); 225 | fw3_setbit(r->_src->flags[1], fw3_to_src_target(r->target)); 226 | } 227 | 228 | return true; 229 | } 230 | 231 | void 232 | fw3_load_rules(struct fw3_state *state, struct uci_package *p, 233 | struct blob_attr *a) 234 | { 235 | struct uci_section *s; 236 | struct uci_element *e; 237 | struct fw3_rule *rule; 238 | struct blob_attr *entry; 239 | unsigned rem; 240 | 241 | INIT_LIST_HEAD(&state->rules); 242 | 243 | blob_for_each_attr(entry, a, rem) { 244 | const char *type; 245 | const char *name = "ubus rule"; 246 | 247 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 248 | continue; 249 | 250 | if (strcmp(type, "rule")) 251 | continue; 252 | 253 | if (!(rule = alloc_rule(state))) 254 | continue; 255 | 256 | if (!fw3_parse_blob_options(rule, fw3_rule_opts, entry, name)) 257 | { 258 | warn_section("rule", rule, NULL, "skipped due to invalid options"); 259 | fw3_free_rule(rule); 260 | continue; 261 | } 262 | 263 | if (!check_rule(state, rule, NULL)) 264 | fw3_free_rule(rule); 265 | } 266 | 267 | uci_foreach_element(&p->sections, e) 268 | { 269 | s = uci_to_section(e); 270 | 271 | if (strcmp(s->type, "rule")) 272 | continue; 273 | 274 | if (!(rule = alloc_rule(state))) 275 | continue; 276 | 277 | if (!fw3_parse_options(rule, fw3_rule_opts, s)) 278 | { 279 | warn_elem(e, "skipped due to invalid options"); 280 | fw3_free_rule(rule); 281 | continue; 282 | } 283 | 284 | if (!check_rule(state, rule, e)) 285 | fw3_free_rule(rule); 286 | } 287 | } 288 | 289 | 290 | static void 291 | append_chain(struct fw3_ipt_rule *r, struct fw3_rule *rule) 292 | { 293 | char chain[32]; 294 | 295 | snprintf(chain, sizeof(chain), "OUTPUT"); 296 | 297 | if (rule->target == FW3_FLAG_NOTRACK) 298 | { 299 | snprintf(chain, sizeof(chain), "zone_%s_notrack", rule->src.name); 300 | } 301 | else if (rule->target == FW3_FLAG_HELPER) 302 | { 303 | snprintf(chain, sizeof(chain), "zone_%s_helper", rule->src.name); 304 | } 305 | else if (rule->target == FW3_FLAG_MARK || rule->target == FW3_FLAG_DSCP) 306 | { 307 | if ((rule->_dest && rule->_src) || 308 | (rule->dest.any && rule->src.any)) 309 | snprintf(chain, sizeof(chain), "FORWARD"); 310 | else if (rule->src.any && rule->_dest) 311 | snprintf(chain, sizeof(chain), "POSTROUTING"); 312 | else if (rule->dest.any && rule->_src) 313 | snprintf(chain, sizeof(chain), "PREROUTING"); 314 | else if (!rule->dest.set && rule->src.set) 315 | snprintf(chain, sizeof(chain), "INPUT"); 316 | else /* if (!rule->src.set) */ 317 | snprintf(chain, sizeof(chain), "OUTPUT"); 318 | } 319 | else 320 | { 321 | if (rule->src.set) 322 | { 323 | if (!rule->src.any) 324 | { 325 | if (rule->dest.set) 326 | snprintf(chain, sizeof(chain), "zone_%s_forward", 327 | rule->src.name); 328 | else 329 | snprintf(chain, sizeof(chain), "zone_%s_input", 330 | rule->src.name); 331 | } 332 | else 333 | { 334 | if (rule->dest.set) 335 | snprintf(chain, sizeof(chain), "FORWARD"); 336 | else 337 | snprintf(chain, sizeof(chain), "INPUT"); 338 | } 339 | } 340 | 341 | if (rule->dest.set && !rule->src.set) 342 | { 343 | if (rule->dest.any) 344 | snprintf(chain, sizeof(chain), "OUTPUT"); 345 | else 346 | snprintf(chain, sizeof(chain), "zone_%s_output", 347 | rule->dest.name); 348 | } 349 | } 350 | 351 | fw3_ipt_rule_append(r, chain); 352 | } 353 | 354 | static void set_target(struct fw3_ipt_rule *r, struct fw3_rule *rule) 355 | { 356 | const char *name; 357 | struct fw3_mark *mark; 358 | char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF")]; 359 | 360 | switch(rule->target) 361 | { 362 | case FW3_FLAG_MARK: 363 | name = rule->set_mark.set ? "--set-mark" : "--set-xmark"; 364 | mark = rule->set_mark.set ? &rule->set_mark : &rule->set_xmark; 365 | snprintf(buf, sizeof(buf), "0x%x/0x%x", mark->mark, mark->mask); 366 | 367 | fw3_ipt_rule_target(r, "MARK"); 368 | fw3_ipt_rule_addarg(r, false, name, buf); 369 | return; 370 | 371 | case FW3_FLAG_DSCP: 372 | snprintf(buf, sizeof(buf), "0x%x", rule->set_dscp.dscp); 373 | 374 | fw3_ipt_rule_target(r, "DSCP"); 375 | fw3_ipt_rule_addarg(r, false, "--set-dscp", buf); 376 | return; 377 | 378 | case FW3_FLAG_NOTRACK: 379 | fw3_ipt_rule_target(r, "CT"); 380 | fw3_ipt_rule_addarg(r, false, "--notrack", NULL); 381 | return; 382 | 383 | case FW3_FLAG_HELPER: 384 | fw3_ipt_rule_target(r, "CT"); 385 | fw3_ipt_rule_addarg(r, false, "--helper", rule->set_helper.ptr->name); 386 | return; 387 | 388 | case FW3_FLAG_ACCEPT: 389 | case FW3_FLAG_DROP: 390 | name = fw3_flag_names[rule->target]; 391 | break; 392 | 393 | default: 394 | name = fw3_flag_names[FW3_FLAG_REJECT]; 395 | break; 396 | } 397 | 398 | if (rule->dest.set && !rule->dest.any) 399 | fw3_ipt_rule_target(r, "zone_%s_dest_%s", rule->dest.name, name); 400 | else if (need_src_action_chain(rule)) 401 | fw3_ipt_rule_target(r, "zone_%s_src_%s", rule->src.name, name); 402 | else if (strcmp(name, "REJECT")) 403 | fw3_ipt_rule_target(r, name); 404 | else 405 | fw3_ipt_rule_target(r, "reject"); 406 | } 407 | 408 | static void 409 | set_comment(struct fw3_ipt_rule *r, const char *name, int num) 410 | { 411 | if (name) 412 | fw3_ipt_rule_comment(r, name); 413 | else 414 | fw3_ipt_rule_comment(r, "@rule[%u]", num); 415 | } 416 | 417 | static void 418 | print_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, 419 | struct fw3_rule *rule, int num, struct fw3_protocol *proto, 420 | struct fw3_address *sip, struct fw3_address *dip, 421 | struct fw3_port *sport, struct fw3_port *dport, 422 | struct fw3_mac *mac, struct fw3_icmptype *icmptype) 423 | { 424 | struct fw3_ipt_rule *r; 425 | struct fw3_device *idev, *odev; 426 | struct list_head empty, *idevices, *odevices; 427 | INIT_LIST_HEAD(&empty); 428 | idevices = odevices = ∅ 429 | 430 | if (!fw3_is_family(sip, handle->family) || 431 | !fw3_is_family(dip, handle->family)) 432 | { 433 | if ((sip && !sip->resolved) || (dip && !dip->resolved)) 434 | info(" ! Skipping due to different family of ip address"); 435 | 436 | return; 437 | } 438 | 439 | if (!fw3_is_family(sip, handle->family) || 440 | !fw3_is_family(dip, handle->family)) 441 | { 442 | if ((sip && !sip->resolved) || (dip && !dip->resolved)) 443 | info(" ! Skipping due to different family of ip address"); 444 | 445 | return; 446 | } 447 | 448 | if (!fw3_is_family(sip, handle->family) || 449 | !fw3_is_family(dip, handle->family)) 450 | { 451 | if ((sip && !sip->resolved) || (dip && !dip->resolved)) 452 | info(" ! Skipping due to different family of ip address"); 453 | 454 | return; 455 | } 456 | 457 | if (proto->protocol == 58 && handle->family == FW3_FAMILY_V4) 458 | { 459 | info(" ! Skipping protocol %s due to different family", 460 | fw3_protoname(proto)); 461 | return; 462 | } 463 | 464 | if (rule->helper.ptr && 465 | !fw3_cthelper_check_proto(rule->helper.ptr, proto)) 466 | { 467 | info(" ! Skipping protocol %s since helper '%s' does not support it", 468 | fw3_protoname(proto), rule->helper.ptr->name); 469 | return; 470 | } 471 | 472 | if (rule->set_helper.ptr && 473 | !fw3_cthelper_check_proto(rule->set_helper.ptr, proto)) 474 | { 475 | info(" ! Skipping protocol %s since helper '%s' does not support it", 476 | fw3_protoname(proto), rule->helper.ptr->name); 477 | return; 478 | } 479 | 480 | if (rule->target == FW3_FLAG_DSCP || rule->target == FW3_FLAG_MARK) 481 | { 482 | if (rule->_src) 483 | idevices = &rule->_src->devices; 484 | if (rule->_dest) 485 | odevices = &rule->_dest->devices; 486 | } 487 | 488 | fw3_foreach(idev, idevices) 489 | fw3_foreach(odev, odevices) 490 | { 491 | r = fw3_ipt_rule_create(handle, proto, idev, odev, sip, dip); 492 | fw3_ipt_rule_sport_dport(r, sport, dport); 493 | fw3_ipt_rule_device(r, rule->device, rule->direction_out); 494 | fw3_ipt_rule_icmptype(r, icmptype); 495 | fw3_ipt_rule_mac(r, mac); 496 | fw3_ipt_rule_ipset(r, &rule->ipset); 497 | fw3_ipt_rule_helper(r, &rule->helper); 498 | fw3_ipt_rule_limit(r, &rule->limit); 499 | fw3_ipt_rule_time(r, &rule->time); 500 | fw3_ipt_rule_mark(r, &rule->mark); 501 | fw3_ipt_rule_dscp(r, &rule->dscp); 502 | set_target(r, rule); 503 | fw3_ipt_rule_extra(r, rule->extra); 504 | set_comment(r, rule->name, num); 505 | append_chain(r, rule); 506 | } 507 | } 508 | 509 | static void 510 | expand_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, 511 | struct fw3_rule *rule, int num) 512 | { 513 | struct fw3_protocol *proto; 514 | struct fw3_address *sip; 515 | struct fw3_address *dip; 516 | struct fw3_port *sport; 517 | struct fw3_port *dport; 518 | struct fw3_mac *mac; 519 | struct fw3_icmptype *icmptype; 520 | 521 | struct list_head *sports = NULL; 522 | struct list_head *dports = NULL; 523 | struct list_head *icmptypes = NULL; 524 | 525 | struct list_head empty; 526 | INIT_LIST_HEAD(&empty); 527 | 528 | if (!fw3_is_family(rule, handle->family)) 529 | return; 530 | 531 | if ((rule->target == FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_RAW) || 532 | (rule->target == FW3_FLAG_HELPER && handle->table != FW3_TABLE_RAW) || 533 | (rule->target == FW3_FLAG_MARK && handle->table != FW3_TABLE_MANGLE) || 534 | (rule->target == FW3_FLAG_DSCP && handle->table != FW3_TABLE_MANGLE) || 535 | (rule->target < FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_FILTER)) 536 | return; 537 | 538 | if (rule->name) 539 | info(" * Rule '%s'", rule->name); 540 | else 541 | info(" * Rule #%u", num); 542 | 543 | if (!fw3_is_family(rule->_src, handle->family) || 544 | !fw3_is_family(rule->_dest, handle->family)) 545 | { 546 | info(" ! Skipping due to different family of zone"); 547 | return; 548 | } 549 | 550 | if (rule->ipset.ptr) 551 | { 552 | if (!fw3_is_family(rule->ipset.ptr, handle->family)) 553 | { 554 | info(" ! Skipping due to different family in ipset"); 555 | return; 556 | } 557 | 558 | if (!fw3_check_ipset(rule->ipset.ptr)) 559 | { 560 | info(" ! Skipping due to missing ipset '%s'", 561 | rule->ipset.ptr->external 562 | ? rule->ipset.ptr->external : rule->ipset.ptr->name); 563 | return; 564 | } 565 | 566 | set(rule->ipset.ptr->flags, handle->family, handle->family); 567 | } 568 | 569 | if (rule->helper.ptr && !fw3_is_family(rule->helper.ptr, handle->family)) 570 | { 571 | info(" ! Skipping due to unsupported family of CT helper"); 572 | return; 573 | } 574 | 575 | if (rule->set_helper.ptr && !fw3_is_family(rule->set_helper.ptr, handle->family)) 576 | { 577 | info(" ! Skipping due to unsupported family of CT helper"); 578 | return; 579 | } 580 | 581 | list_for_each_entry(proto, &rule->proto, list) 582 | { 583 | /* icmp / ipv6-icmp */ 584 | if (proto->protocol == 1 || proto->protocol == 58) 585 | { 586 | sports = ∅ 587 | dports = ∅ 588 | icmptypes = &rule->icmp_type; 589 | } 590 | else 591 | { 592 | sports = &rule->port_src; 593 | dports = &rule->port_dest; 594 | icmptypes = ∅ 595 | } 596 | 597 | fw3_foreach(sip, &rule->ip_src) 598 | fw3_foreach(dip, &rule->ip_dest) 599 | fw3_foreach(sport, sports) 600 | fw3_foreach(dport, dports) 601 | fw3_foreach(mac, &rule->mac_src) 602 | fw3_foreach(icmptype, icmptypes) 603 | print_rule(handle, state, rule, num, proto, sip, dip, 604 | sport, dport, mac, icmptype); 605 | } 606 | } 607 | 608 | void 609 | fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state) 610 | { 611 | int num = 0; 612 | struct fw3_rule *rule; 613 | 614 | list_for_each_entry(rule, &state->rules, list) 615 | expand_rule(handle, state, rule, num++); 616 | } 617 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #define _GNU_SOURCE 20 | 21 | #include 22 | #include 23 | 24 | #include "utils.h" 25 | #include "options.h" 26 | 27 | #include "zones.h" 28 | #include "ipsets.h" 29 | 30 | 31 | static int fw3_lock_fd = -1; 32 | static pid_t pipe_pid = -1; 33 | static FILE *pipe_fd = NULL; 34 | 35 | bool fw3_pr_debug = false; 36 | 37 | 38 | static void 39 | warn_elem_section_name(struct uci_section *s, bool find_name) 40 | { 41 | int i = 0; 42 | struct uci_option *o; 43 | struct uci_element *tmp; 44 | 45 | if (s->anonymous) 46 | { 47 | uci_foreach_element(&s->package->sections, tmp) 48 | { 49 | if (strcmp(uci_to_section(tmp)->type, s->type)) 50 | continue; 51 | 52 | if (&s->e == tmp) 53 | break; 54 | 55 | i++; 56 | } 57 | 58 | fprintf(stderr, "@%s[%d]", s->type, i); 59 | 60 | if (find_name) 61 | { 62 | uci_foreach_element(&s->options, tmp) 63 | { 64 | o = uci_to_option(tmp); 65 | 66 | if (!strcmp(tmp->name, "name") && (o->type == UCI_TYPE_STRING)) 67 | { 68 | fprintf(stderr, " (%s)", o->v.string); 69 | break; 70 | } 71 | } 72 | } 73 | } 74 | else 75 | { 76 | fprintf(stderr, "'%s'", s->e.name); 77 | } 78 | 79 | if (find_name) 80 | fprintf(stderr, " "); 81 | } 82 | 83 | void 84 | warn_elem(struct uci_element *e, const char *format, ...) 85 | { 86 | if (e->type == UCI_TYPE_SECTION) 87 | { 88 | fprintf(stderr, "Warning: Section "); 89 | warn_elem_section_name(uci_to_section(e), true); 90 | } 91 | else if (e->type == UCI_TYPE_OPTION) 92 | { 93 | fprintf(stderr, "Warning: Option "); 94 | warn_elem_section_name(uci_to_option(e)->section, false); 95 | fprintf(stderr, ".%s ", e->name); 96 | } 97 | 98 | va_list argptr; 99 | va_start(argptr, format); 100 | vfprintf(stderr, format, argptr); 101 | va_end(argptr); 102 | 103 | fprintf(stderr, "\n"); 104 | } 105 | 106 | void 107 | warn(const char* format, ...) 108 | { 109 | fprintf(stderr, "Warning: "); 110 | va_list argptr; 111 | va_start(argptr, format); 112 | vfprintf(stderr, format, argptr); 113 | va_end(argptr); 114 | fprintf(stderr, "\n"); 115 | } 116 | 117 | void 118 | error(const char* format, ...) 119 | { 120 | fprintf(stderr, "Error: "); 121 | va_list argptr; 122 | va_start(argptr, format); 123 | vfprintf(stderr, format, argptr); 124 | va_end(argptr); 125 | fprintf(stderr, "\n"); 126 | 127 | exit(1); 128 | } 129 | 130 | void 131 | info(const char* format, ...) 132 | { 133 | va_list argptr; 134 | va_start(argptr, format); 135 | vfprintf(stderr, format, argptr); 136 | va_end(argptr); 137 | fprintf(stderr, "\n"); 138 | } 139 | 140 | void * 141 | fw3_alloc(size_t size) 142 | { 143 | void *mem; 144 | 145 | mem = calloc(1, size); 146 | 147 | if (!mem) 148 | error("Out of memory while allocating %zd bytes", size); 149 | 150 | return mem; 151 | } 152 | 153 | char * 154 | fw3_strdup(const char *s) 155 | { 156 | char *ns; 157 | 158 | ns = strdup(s); 159 | 160 | if (!ns) 161 | error("Out of memory while duplicating string '%s'", s); 162 | 163 | return ns; 164 | } 165 | 166 | const char * 167 | fw3_find_command(const char *cmd) 168 | { 169 | struct stat s; 170 | int plen = 0, clen = strlen(cmd) + 1; 171 | char *search, *p; 172 | static char path[PATH_MAX]; 173 | 174 | if (!stat(cmd, &s) && S_ISREG(s.st_mode)) 175 | return cmd; 176 | 177 | search = getenv("PATH"); 178 | 179 | if (!search) 180 | search = "/bin:/usr/bin:/sbin:/usr/sbin"; 181 | 182 | p = search; 183 | 184 | do 185 | { 186 | if (*p != ':' && *p != '\0') 187 | continue; 188 | 189 | plen = p - search; 190 | 191 | if ((plen + clen) >= sizeof(path)) 192 | continue; 193 | 194 | snprintf(path, sizeof(path), "%.*s/%s", plen, search, cmd); 195 | 196 | if (!stat(path, &s) && S_ISREG(s.st_mode)) 197 | return path; 198 | 199 | search = p + 1; 200 | } 201 | while (*p++); 202 | 203 | return NULL; 204 | } 205 | 206 | bool 207 | fw3_stdout_pipe(void) 208 | { 209 | pipe_fd = stdout; 210 | return true; 211 | } 212 | 213 | bool 214 | __fw3_command_pipe(bool silent, const char *command, ...) 215 | { 216 | pid_t pid; 217 | va_list argp; 218 | int pfds[2]; 219 | int argn; 220 | char *arg, **args, **tmp; 221 | 222 | command = fw3_find_command(command); 223 | 224 | if (!command) 225 | return false; 226 | 227 | if (pipe(pfds)) 228 | return false; 229 | 230 | argn = 2; 231 | args = calloc(argn, sizeof(arg)); 232 | 233 | if (!args) 234 | return false; 235 | 236 | args[0] = (char *)command; 237 | args[1] = NULL; 238 | 239 | va_start(argp, command); 240 | 241 | while ((arg = va_arg(argp, char *)) != NULL) 242 | { 243 | tmp = realloc(args, ++argn * sizeof(arg)); 244 | 245 | if (!tmp) 246 | break; 247 | 248 | args = tmp; 249 | args[argn-2] = arg; 250 | args[argn-1] = NULL; 251 | } 252 | 253 | va_end(argp); 254 | 255 | switch ((pid = fork())) 256 | { 257 | case -1: 258 | free(args); 259 | return false; 260 | 261 | case 0: 262 | dup2(pfds[0], 0); 263 | 264 | close(pfds[0]); 265 | close(pfds[1]); 266 | 267 | close(1); 268 | 269 | if (silent) 270 | close(2); 271 | 272 | execv(command, args); 273 | 274 | default: 275 | signal(SIGPIPE, SIG_IGN); 276 | pipe_pid = pid; 277 | close(pfds[0]); 278 | fcntl(pfds[1], F_SETFD, fcntl(pfds[1], F_GETFD) | FD_CLOEXEC); 279 | } 280 | 281 | pipe_fd = fdopen(pfds[1], "w"); 282 | free(args); 283 | return true; 284 | } 285 | 286 | void 287 | fw3_pr(const char *fmt, ...) 288 | { 289 | va_list args; 290 | 291 | if (fw3_pr_debug && pipe_fd != stdout) 292 | { 293 | va_start(args, fmt); 294 | vfprintf(stderr, fmt, args); 295 | va_end(args); 296 | } 297 | 298 | va_start(args, fmt); 299 | vfprintf(pipe_fd, fmt, args); 300 | va_end(args); 301 | } 302 | 303 | void 304 | fw3_command_close(void) 305 | { 306 | if (pipe_fd && pipe_fd != stdout) 307 | fclose(pipe_fd); 308 | 309 | if (pipe_pid > -1) 310 | waitpid(pipe_pid, NULL, 0); 311 | 312 | signal(SIGPIPE, SIG_DFL); 313 | 314 | pipe_fd = NULL; 315 | pipe_pid = -1; 316 | } 317 | 318 | static bool 319 | file_contains(const char *path, const char *str) 320 | { 321 | FILE *f; 322 | char line[12]; 323 | bool seen = false; 324 | 325 | if (!(f = fopen(path, "r"))) 326 | return false; 327 | 328 | while (fgets(line, sizeof(line), f)) 329 | { 330 | if (!strncmp(line, str, strlen(str))) 331 | { 332 | seen = true; 333 | break; 334 | } 335 | } 336 | 337 | fclose(f); 338 | 339 | return seen; 340 | } 341 | 342 | bool 343 | fw3_has_target(const bool ipv6, const char *target) 344 | { 345 | const char *path = ipv6 346 | ? "/proc/net/ip6_tables_targets" : "/proc/net/ip_tables_targets"; 347 | 348 | return file_contains(path, target); 349 | } 350 | 351 | bool 352 | fw3_lock_path(int *fd, const char *path) 353 | { 354 | int lock_fd = open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); 355 | 356 | if (lock_fd < 0) 357 | { 358 | warn("Cannot create lock file %s: %s", path, strerror(errno)); 359 | return false; 360 | } 361 | 362 | if (flock(lock_fd, LOCK_EX)) 363 | { 364 | warn("Cannot acquire exclusive lock: %s", strerror(errno)); 365 | close(lock_fd); 366 | return false; 367 | } 368 | 369 | *fd = lock_fd; 370 | 371 | return true; 372 | } 373 | 374 | bool 375 | fw3_lock() 376 | { 377 | return fw3_lock_path(&fw3_lock_fd, FW3_LOCKFILE); 378 | } 379 | 380 | 381 | void 382 | fw3_unlock_path(int *fd, const char *lockpath) 383 | { 384 | if (*fd < 0) 385 | return; 386 | 387 | if (flock(*fd, LOCK_UN)) 388 | warn("Cannot release exclusive lock: %s", strerror(errno)); 389 | 390 | close(*fd); 391 | 392 | *fd = -1; 393 | } 394 | 395 | 396 | void 397 | fw3_unlock(void) 398 | { 399 | fw3_unlock_path(&fw3_lock_fd, FW3_LOCKFILE); 400 | } 401 | 402 | 403 | static void 404 | write_defaults_uci(struct uci_context *ctx, struct fw3_defaults *d, 405 | struct uci_package *dest) 406 | { 407 | char buf[sizeof("0xffffffff")]; 408 | struct uci_ptr ptr = { .p = dest }; 409 | 410 | uci_add_section(ctx, dest, "defaults", &ptr.s); 411 | 412 | ptr.o = NULL; 413 | ptr.option = "input"; 414 | ptr.value = fw3_flag_names[d->policy_input]; 415 | uci_set(ctx, &ptr); 416 | 417 | ptr.o = NULL; 418 | ptr.option = "output"; 419 | ptr.value = fw3_flag_names[d->policy_output]; 420 | uci_set(ctx, &ptr); 421 | 422 | ptr.o = NULL; 423 | ptr.option = "forward"; 424 | ptr.value = fw3_flag_names[d->policy_forward]; 425 | uci_set(ctx, &ptr); 426 | 427 | snprintf(buf, sizeof(buf), "0x%x", d->flags[0]); 428 | ptr.o = NULL; 429 | ptr.option = "__flags_v4"; 430 | ptr.value = buf; 431 | uci_set(ctx, &ptr); 432 | 433 | snprintf(buf, sizeof(buf), "0x%x", d->flags[1]); 434 | ptr.o = NULL; 435 | ptr.option = "__flags_v6"; 436 | ptr.value = buf; 437 | uci_set(ctx, &ptr); 438 | } 439 | 440 | static void 441 | write_zone_uci(struct uci_context *ctx, struct fw3_zone *z, 442 | struct uci_package *dest, struct ifaddrs *ifaddr) 443 | { 444 | struct fw3_device *dev; 445 | struct fw3_address *sub; 446 | struct ifaddrs *ifa; 447 | enum fw3_family fam = FW3_FAMILY_ANY; 448 | 449 | char *p, buf[INET6_ADDRSTRLEN]; 450 | 451 | struct uci_ptr ptr = { .p = dest }; 452 | 453 | if (!z->enabled) 454 | return; 455 | 456 | if (fw3_no_table(z->flags[0]) && !fw3_no_table(z->flags[1])) 457 | fam = FW3_FAMILY_V6; 458 | else if (!fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1])) 459 | fam = FW3_FAMILY_V4; 460 | else if (fw3_no_table(z->flags[0]) && fw3_no_table(z->flags[1])) 461 | return; 462 | 463 | uci_add_section(ctx, dest, "zone", &ptr.s); 464 | 465 | ptr.o = NULL; 466 | ptr.option = "name"; 467 | ptr.value = z->name; 468 | uci_set(ctx, &ptr); 469 | 470 | ptr.o = NULL; 471 | ptr.option = "input"; 472 | ptr.value = fw3_flag_names[z->policy_input]; 473 | uci_set(ctx, &ptr); 474 | 475 | ptr.o = NULL; 476 | ptr.option = "output"; 477 | ptr.value = fw3_flag_names[z->policy_output]; 478 | uci_set(ctx, &ptr); 479 | 480 | ptr.o = NULL; 481 | ptr.option = "forward"; 482 | ptr.value = fw3_flag_names[z->policy_forward]; 483 | uci_set(ctx, &ptr); 484 | 485 | ptr.o = NULL; 486 | ptr.option = "masq"; 487 | ptr.value = z->masq ? "1" : "0"; 488 | uci_set(ctx, &ptr); 489 | 490 | ptr.o = NULL; 491 | ptr.option = "mtu_fix"; 492 | ptr.value = z->mtu_fix ? "1" : "0"; 493 | uci_set(ctx, &ptr); 494 | 495 | ptr.o = NULL; 496 | ptr.option = "custom_chains"; 497 | ptr.value = z->custom_chains ? "1" : "0"; 498 | uci_set(ctx, &ptr); 499 | 500 | if (fam != FW3_FAMILY_ANY) 501 | { 502 | ptr.o = NULL; 503 | ptr.option = "family"; 504 | ptr.value = fw3_flag_names[fam]; 505 | uci_set(ctx, &ptr); 506 | } 507 | 508 | ptr.o = NULL; 509 | ptr.option = "device"; 510 | 511 | fw3_foreach(dev, &z->devices) 512 | { 513 | char *ep; 514 | 515 | if (!dev) 516 | continue; 517 | 518 | p = buf; 519 | ep = buf + sizeof(buf); 520 | 521 | if (dev->invert) 522 | p += snprintf(p, ep - p, "!"); 523 | 524 | if (*dev->network) 525 | p += snprintf(p, ep - p, "%s@%s", dev->name, dev->network); 526 | else 527 | p += snprintf(p, ep - p, "%s", dev->name); 528 | 529 | ptr.value = buf; 530 | uci_add_list(ctx, &ptr); 531 | } 532 | 533 | ptr.o = NULL; 534 | ptr.option = "subnet"; 535 | 536 | fw3_foreach(sub, &z->subnets) 537 | { 538 | if (!sub) 539 | continue; 540 | 541 | ptr.value = fw3_address_to_string(sub, true, false); 542 | uci_add_list(ctx, &ptr); 543 | } 544 | 545 | ptr.o = NULL; 546 | ptr.option = "__addrs"; 547 | 548 | fw3_foreach(dev, &z->devices) 549 | { 550 | if (!dev) 551 | continue; 552 | 553 | for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) 554 | { 555 | if (!ifa->ifa_addr || strcmp(dev->name, ifa->ifa_name)) 556 | continue; 557 | 558 | if (ifa->ifa_addr->sa_family == AF_INET) 559 | inet_ntop(AF_INET, 560 | &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, 561 | buf, sizeof(buf)); 562 | else if (ifa->ifa_addr->sa_family == AF_INET6) 563 | inet_ntop(AF_INET6, 564 | &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, 565 | buf, sizeof(buf)); 566 | else 567 | continue; 568 | 569 | ptr.value = buf; 570 | uci_add_list(ctx, &ptr); 571 | } 572 | } 573 | 574 | if (z->extra_src) 575 | { 576 | ptr.o = NULL; 577 | ptr.option = "extra_src"; 578 | ptr.value = z->extra_src; 579 | uci_set(ctx, &ptr); 580 | } 581 | 582 | if (z->extra_dest) 583 | { 584 | ptr.o = NULL; 585 | ptr.option = "extra_dest"; 586 | ptr.value = z->extra_dest; 587 | uci_set(ctx, &ptr); 588 | } 589 | 590 | snprintf(buf, sizeof(buf), "0x%x", z->flags[0]); 591 | ptr.o = NULL; 592 | ptr.option = "__flags_v4"; 593 | ptr.value = buf; 594 | uci_set(ctx, &ptr); 595 | 596 | snprintf(buf, sizeof(buf), "0x%x", z->flags[1]); 597 | ptr.o = NULL; 598 | ptr.option = "__flags_v6"; 599 | ptr.value = buf; 600 | uci_set(ctx, &ptr); 601 | } 602 | 603 | static void 604 | write_ipset_uci(struct uci_context *ctx, struct fw3_ipset *s, 605 | struct uci_package *dest) 606 | { 607 | struct fw3_ipset_datatype *type; 608 | 609 | char buf[sizeof("65535-65535")]; 610 | 611 | struct uci_ptr ptr = { .p = dest }; 612 | 613 | if (!s->enabled || s->external) 614 | return; 615 | 616 | uci_add_section(ctx, dest, "ipset", &ptr.s); 617 | 618 | ptr.o = NULL; 619 | ptr.option = "name"; 620 | ptr.value = s->name; 621 | uci_set(ctx, &ptr); 622 | 623 | ptr.o = NULL; 624 | ptr.option = "family"; 625 | if (s->family == FW3_FAMILY_V4) 626 | ptr.value = "ipv4"; 627 | else 628 | ptr.value = "ipv6"; 629 | uci_set(ctx, &ptr); 630 | 631 | ptr.o = NULL; 632 | ptr.option = "storage"; 633 | ptr.value = fw3_ipset_method_names[s->method]; 634 | uci_set(ctx, &ptr); 635 | 636 | list_for_each_entry(type, &s->datatypes, list) 637 | { 638 | snprintf(buf, sizeof(buf), "%s_%s", type->dir, fw3_ipset_type_names[type->type]); 639 | ptr.o = NULL; 640 | ptr.option = "match"; 641 | ptr.value = buf; 642 | uci_add_list(ctx, &ptr); 643 | } 644 | 645 | if (s->iprange.set) 646 | { 647 | ptr.o = NULL; 648 | ptr.option = "iprange"; 649 | ptr.value = fw3_address_to_string(&s->iprange, false, false); 650 | uci_set(ctx, &ptr); 651 | } 652 | 653 | if (s->portrange.set) 654 | { 655 | snprintf(buf, sizeof(buf), "%u-%u", s->portrange.port_min, s->portrange.port_max); 656 | ptr.o = NULL; 657 | ptr.option = "portrange"; 658 | ptr.value = buf; 659 | uci_set(ctx, &ptr); 660 | } 661 | } 662 | 663 | void 664 | fw3_write_statefile(void *state) 665 | { 666 | FILE *sf; 667 | struct fw3_state *s = state; 668 | struct fw3_zone *z; 669 | struct fw3_ipset *i; 670 | struct ifaddrs *ifaddr; 671 | 672 | struct uci_package *p; 673 | 674 | if (fw3_no_family(s->defaults.flags[0]) && 675 | fw3_no_family(s->defaults.flags[1])) 676 | { 677 | unlink(FW3_STATEFILE); 678 | } 679 | else 680 | { 681 | sf = fopen(FW3_STATEFILE, "w+"); 682 | 683 | if (!sf) 684 | { 685 | warn("Cannot create state %s: %s", FW3_STATEFILE, strerror(errno)); 686 | return; 687 | } 688 | 689 | if (getifaddrs(&ifaddr)) 690 | { 691 | warn("Cannot get interface addresses: %s", strerror(errno)); 692 | ifaddr = NULL; 693 | } 694 | 695 | if ((p = uci_lookup_package(s->uci, "fw3_state")) != NULL) 696 | uci_unload(s->uci, p); 697 | 698 | uci_import(s->uci, sf, "fw3_state", NULL, true); 699 | 700 | if ((p = uci_lookup_package(s->uci, "fw3_state")) != NULL) 701 | { 702 | write_defaults_uci(s->uci, &s->defaults, p); 703 | 704 | list_for_each_entry(z, &s->zones, list) 705 | write_zone_uci(s->uci, z, p, ifaddr); 706 | 707 | list_for_each_entry(i, &s->ipsets, list) 708 | write_ipset_uci(s->uci, i, p); 709 | 710 | uci_export(s->uci, sf, p, true); 711 | uci_unload(s->uci, p); 712 | } 713 | 714 | fsync(fileno(sf)); 715 | fclose(sf); 716 | 717 | if (ifaddr) 718 | freeifaddrs(ifaddr); 719 | } 720 | } 721 | 722 | 723 | void 724 | fw3_free_object(void *obj, const void *opts) 725 | { 726 | const struct fw3_option *ol; 727 | struct list_head *list, *cur, *tmp; 728 | 729 | for (ol = opts; ol->name; ol++) 730 | { 731 | if (!ol->elem_size) 732 | continue; 733 | 734 | list = (struct list_head *)((char *)obj + ol->offset); 735 | list_for_each_safe(cur, tmp, list) 736 | { 737 | list_del(cur); 738 | free(cur); 739 | } 740 | } 741 | 742 | free(obj); 743 | } 744 | 745 | void 746 | fw3_free_list(struct list_head *head) 747 | { 748 | struct list_head *entry, *tmp; 749 | 750 | if (!head) 751 | return; 752 | 753 | list_for_each_safe(entry, tmp, head) 754 | { 755 | list_del(entry); 756 | free(entry); 757 | } 758 | 759 | free(head); 760 | } 761 | 762 | bool 763 | fw3_hotplug(bool add, void *zone, void *device) 764 | { 765 | struct fw3_zone *z = zone; 766 | struct fw3_device *d = device; 767 | 768 | if (!*d->network) 769 | return false; 770 | 771 | switch (fork()) 772 | { 773 | case -1: 774 | warn("Unable to fork(): %s\n", strerror(errno)); 775 | return false; 776 | 777 | case 0: 778 | break; 779 | 780 | default: 781 | return true; 782 | } 783 | 784 | close(0); 785 | close(1); 786 | close(2); 787 | if (chdir("/")) {}; 788 | 789 | clearenv(); 790 | setenv("ACTION", add ? "add" : "remove", 1); 791 | setenv("ZONE", z->name, 1); 792 | setenv("INTERFACE", d->network, 1); 793 | setenv("DEVICE", d->name, 1); 794 | 795 | execl(FW3_HOTPLUG, FW3_HOTPLUG, "firewall", NULL); 796 | 797 | /* unreached */ 798 | return false; 799 | } 800 | 801 | int 802 | fw3_netmask2bitlen(int family, void *mask) 803 | { 804 | int bits; 805 | struct in_addr *v4; 806 | struct in6_addr *v6; 807 | 808 | if (family == FW3_FAMILY_V6) 809 | for (bits = 0, v6 = mask; 810 | bits < 128 && (v6->s6_addr[bits / 8] << (bits % 8)) & 128; 811 | bits++); 812 | else 813 | for (bits = 0, v4 = mask; 814 | bits < 32 && (ntohl(v4->s_addr) << bits) & 0x80000000; 815 | bits++); 816 | 817 | return bits; 818 | } 819 | 820 | bool 821 | fw3_bitlen2netmask(int family, int bits, void *mask) 822 | { 823 | int i; 824 | uint8_t rem, b; 825 | struct in_addr *v4; 826 | struct in6_addr *v6; 827 | 828 | if (family == FW3_FAMILY_V6) 829 | { 830 | if (bits < -128 || bits > 128) 831 | return false; 832 | 833 | v6 = mask; 834 | rem = abs(bits); 835 | 836 | for (i = 0; i < sizeof(v6->s6_addr); i++) 837 | { 838 | b = (rem > 8) ? 8 : rem; 839 | v6->s6_addr[i] = (uint8_t)(0xFF << (8 - b)); 840 | rem -= b; 841 | } 842 | 843 | if (bits < 0) 844 | for (i = 0; i < sizeof(v6->s6_addr); i++) 845 | v6->s6_addr[i] = ~v6->s6_addr[i]; 846 | } 847 | else 848 | { 849 | if (bits < -32 || bits > 32) 850 | return false; 851 | 852 | v4 = mask; 853 | v4->s_addr = bits ? htonl(~((1 << (32 - abs(bits))) - 1)) : 0; 854 | 855 | if (bits < 0) 856 | v4->s_addr = ~v4->s_addr; 857 | } 858 | 859 | return true; 860 | } 861 | 862 | void 863 | fw3_flush_conntrack(void *state) 864 | { 865 | bool found; 866 | struct fw3_state *s = state; 867 | struct fw3_address *addr; 868 | struct fw3_device *dev; 869 | struct fw3_zone *zone; 870 | struct ifaddrs *ifaddr, *ifa; 871 | struct sockaddr_in *sin; 872 | struct sockaddr_in6 *sin6; 873 | char buf[INET6_ADDRSTRLEN]; 874 | FILE *ct; 875 | 876 | if (!state) 877 | { 878 | if ((ct = fopen("/proc/net/nf_conntrack", "w")) != NULL) 879 | { 880 | info(" * Flushing conntrack table ..."); 881 | 882 | fwrite("f\n", 1, 2, ct); 883 | fclose(ct); 884 | } 885 | 886 | return; 887 | } 888 | 889 | if (getifaddrs(&ifaddr)) 890 | { 891 | warn("Cannot get interface addresses: %s", strerror(errno)); 892 | return; 893 | } 894 | 895 | if ((ct = fopen("/proc/net/nf_conntrack", "w")) != NULL) 896 | { 897 | list_for_each_entry(zone, &s->zones, list) 898 | list_for_each_entry(addr, &zone->old_addrs, list) 899 | { 900 | found = false; 901 | 902 | list_for_each_entry(dev, &zone->devices, list) 903 | { 904 | for (ifa = ifaddr; ifa && !found; ifa = ifa->ifa_next) 905 | { 906 | if (!ifa->ifa_addr || strcmp(dev->name, ifa->ifa_name)) 907 | continue; 908 | 909 | sin = (struct sockaddr_in *)ifa->ifa_addr; 910 | sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; 911 | 912 | if (addr->family == FW3_FAMILY_V4 && 913 | sin->sin_family == AF_INET) 914 | { 915 | found = !memcmp(&addr->address.v4, &sin->sin_addr, 916 | sizeof(sin->sin_addr)); 917 | } 918 | else if (addr->family == FW3_FAMILY_V6 && 919 | sin6->sin6_family == AF_INET6) 920 | { 921 | found = !memcmp(&addr->address.v6, &sin6->sin6_addr, 922 | sizeof(sin6->sin6_addr)); 923 | } 924 | } 925 | 926 | if (found) 927 | break; 928 | } 929 | 930 | if (!found) 931 | { 932 | inet_ntop(addr->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, 933 | &addr->address.v4, buf, sizeof(buf)); 934 | 935 | info(" * Flushing conntrack: %s", buf); 936 | fprintf(ct, "%s\n", buf); 937 | } 938 | } 939 | 940 | fclose(ct); 941 | } 942 | 943 | freeifaddrs(ifaddr); 944 | } 945 | 946 | bool fw3_attr_parse_name_type(struct blob_attr *entry, const char **name, const char **type) 947 | { 948 | struct blob_attr *opt; 949 | unsigned orem; 950 | 951 | if (!type || !name) 952 | return false; 953 | 954 | *type = NULL; 955 | 956 | blobmsg_for_each_attr(opt, entry, orem) 957 | if (!strcmp(blobmsg_name(opt), "type")) 958 | *type = blobmsg_get_string(opt); 959 | else if (!strcmp(blobmsg_name(opt), "name")) 960 | *name = blobmsg_get_string(opt); 961 | 962 | return *type != NULL ? true : false; 963 | } 964 | 965 | const char * 966 | fw3_protoname(void *proto) 967 | { 968 | static char buf[sizeof("4294967295")]; 969 | struct fw3_protocol *p = proto; 970 | struct protoent *pe; 971 | 972 | if (!p) 973 | return "?"; 974 | 975 | pe = getprotobynumber(p->protocol); 976 | 977 | if (!pe) 978 | { 979 | snprintf(buf, sizeof(buf), "%u", p->protocol); 980 | return buf; 981 | } 982 | 983 | return pe->p_name; 984 | } 985 | 986 | bool 987 | fw3_check_loopback_dev(const char *name) 988 | { 989 | struct ifreq ifr; 990 | int s; 991 | bool rv = false; 992 | 993 | s = socket(AF_LOCAL, SOCK_DGRAM, 0); 994 | 995 | if (s < 0) 996 | return false; 997 | 998 | memset(&ifr, 0, sizeof(ifr)); 999 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", name); 1000 | 1001 | if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) { 1002 | if (ifr.ifr_flags & IFF_LOOPBACK) 1003 | rv = true; 1004 | } 1005 | 1006 | close(s); 1007 | 1008 | return rv; 1009 | } 1010 | 1011 | bool 1012 | fw3_check_loopback_addr(struct fw3_address *addr) 1013 | { 1014 | if (addr->family == FW3_FAMILY_V4 && 1015 | (ntohl(addr->address.v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) 1016 | return true; 1017 | 1018 | if (addr->family == FW3_FAMILY_V6 && !addr->range && 1019 | fw3_netmask2bitlen(FW3_FAMILY_V6, &addr->mask.v6) == 128 && 1020 | IN6_IS_ADDR_LOOPBACK(&addr->address.v6)) 1021 | return true; 1022 | 1023 | return false; 1024 | } 1025 | -------------------------------------------------------------------------------- /redirects.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewall3 - 3rd OpenWrt UCI firewall implementation 3 | * 4 | * Copyright (C) 2013-2018 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "redirects.h" 20 | 21 | 22 | const struct fw3_option fw3_redirect_opts[] = { 23 | FW3_OPT("enabled", bool, redirect, enabled), 24 | 25 | FW3_OPT("name", string, redirect, name), 26 | FW3_OPT("family", family, redirect, family), 27 | 28 | FW3_OPT("src", device, redirect, src), 29 | FW3_OPT("dest", device, redirect, dest), 30 | 31 | FW3_OPT("ipset", setmatch, redirect, ipset), 32 | FW3_OPT("helper", cthelper, redirect, helper), 33 | 34 | FW3_LIST("proto", protocol, redirect, proto), 35 | 36 | FW3_OPT("src_ip", network, redirect, ip_src), 37 | FW3_LIST("src_mac", mac, redirect, mac_src), 38 | FW3_OPT("src_port", port, redirect, port_src), 39 | 40 | FW3_OPT("src_dip", network, redirect, ip_dest), 41 | FW3_OPT("src_dport", port, redirect, port_dest), 42 | 43 | FW3_OPT("dest_ip", network, redirect, ip_redir), 44 | FW3_OPT("dest_port", port, redirect, port_redir), 45 | 46 | FW3_OPT("extra", string, redirect, extra), 47 | 48 | FW3_OPT("limit", limit, redirect, limit), 49 | FW3_OPT("limit_burst", int, redirect, limit.burst), 50 | 51 | FW3_OPT("utc_time", bool, redirect, time.utc), 52 | FW3_OPT("start_date", date, redirect, time.datestart), 53 | FW3_OPT("stop_date", date, redirect, time.datestop), 54 | FW3_OPT("start_time", time, redirect, time.timestart), 55 | FW3_OPT("stop_time", time, redirect, time.timestop), 56 | FW3_OPT("weekdays", weekdays, redirect, time.weekdays), 57 | FW3_OPT("monthdays", monthdays, redirect, time.monthdays), 58 | 59 | FW3_OPT("mark", mark, redirect, mark), 60 | 61 | FW3_OPT("reflection", bool, redirect, reflection), 62 | FW3_OPT("reflection_src", reflection_source, 63 | redirect, reflection_src), 64 | FW3_LIST("reflection_zone", device, redirect, reflection_zones), 65 | 66 | FW3_OPT("target", target, redirect, target), 67 | 68 | { } 69 | }; 70 | 71 | 72 | static bool 73 | check_families(struct uci_element *e, struct fw3_redirect *r) 74 | { 75 | if (r->family == FW3_FAMILY_ANY) 76 | return true; 77 | 78 | if (r->_src && r->_src->family && r->_src->family != r->family) 79 | { 80 | warn_elem(e, "refers to source zone with different family"); 81 | return false; 82 | } 83 | 84 | if (r->_dest && r->_dest->family && r->_dest->family != r->family) 85 | { 86 | warn_elem(e, "refers to destination zone with different family"); 87 | return false; 88 | } 89 | 90 | if (r->ipset.ptr && r->ipset.ptr->family && 91 | r->ipset.ptr->family != r->family) 92 | { 93 | warn_elem(e, "refers to ipset with different family"); 94 | return false; 95 | } 96 | 97 | if (r->helper.ptr && r->helper.ptr->family && 98 | r->helper.ptr->family != r->family) 99 | { 100 | warn_elem(e, "refers to CT helper not supporting family"); 101 | return false; 102 | } 103 | 104 | if (r->ip_src.family && r->ip_src.family != r->family) 105 | { 106 | warn_elem(e, "uses source ip with different family"); 107 | return false; 108 | } 109 | 110 | if (r->ip_dest.family && r->ip_dest.family != r->family) 111 | { 112 | warn_elem(e, "uses destination ip with different family"); 113 | return false; 114 | } 115 | 116 | if (r->ip_redir.family && r->ip_redir.family != r->family) 117 | { 118 | warn_elem(e, "uses redirect ip with different family"); 119 | return false; 120 | } 121 | 122 | return true; 123 | } 124 | 125 | static bool 126 | compare_addr(struct fw3_address *a, struct fw3_address *b) 127 | { 128 | if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4) 129 | return false; 130 | 131 | return ((a->address.v4.s_addr & a->mask.v4.s_addr) == 132 | (b->address.v4.s_addr & a->mask.v4.s_addr)); 133 | } 134 | 135 | static bool 136 | resolve_dest(struct uci_element *e, struct fw3_redirect *redir, 137 | struct fw3_state *state) 138 | { 139 | struct fw3_zone *zone; 140 | struct fw3_address *addr; 141 | struct list_head *addrs; 142 | 143 | if (!redir->ip_redir.set) 144 | return false; 145 | 146 | list_for_each_entry(zone, &state->zones, list) 147 | { 148 | addrs = fw3_resolve_zone_addresses(zone, NULL); 149 | 150 | if (!addrs) 151 | continue; 152 | 153 | list_for_each_entry(addr, addrs, list) 154 | { 155 | if (!compare_addr(addr, &redir->ip_redir)) 156 | continue; 157 | 158 | snprintf(redir->dest.name, sizeof(redir->dest.name), "%s", zone->name); 159 | redir->dest.set = true; 160 | redir->_dest = zone; 161 | 162 | break; 163 | } 164 | 165 | fw3_free_list(addrs); 166 | 167 | if (redir->_dest) 168 | return true; 169 | } 170 | 171 | return false; 172 | } 173 | 174 | static bool 175 | check_local(struct uci_element *e, struct fw3_redirect *redir, 176 | struct fw3_state *state) 177 | { 178 | if (redir->target != FW3_FLAG_DNAT) 179 | return false; 180 | 181 | if (!redir->ip_redir.set) 182 | redir->local = true; 183 | 184 | return redir->local; 185 | } 186 | 187 | static void 188 | select_helper(struct fw3_state *state, struct fw3_redirect *redir) 189 | { 190 | struct fw3_protocol *proto; 191 | struct fw3_cthelper *helper; 192 | int n_matches = 0; 193 | 194 | if (!state->defaults.auto_helper) 195 | return; 196 | 197 | if (!redir->_src || redir->target != FW3_FLAG_DNAT) 198 | return; 199 | 200 | if (!redir->port_redir.set || redir->port_redir.invert) 201 | return; 202 | 203 | if (redir->helper.set || redir->helper.ptr) 204 | return; 205 | 206 | if (list_empty(&redir->proto)) 207 | return; 208 | 209 | list_for_each_entry(proto, &redir->proto, list) 210 | { 211 | helper = fw3_lookup_cthelper_by_proto_port(state, proto, &redir->port_redir); 212 | 213 | if (helper) 214 | n_matches++; 215 | } 216 | 217 | if (n_matches != 1) 218 | return; 219 | 220 | /* store pointer to auto-selected helper but set ".set" flag to false, 221 | * to allow later code to decide between configured or auto-selected 222 | * helpers */ 223 | redir->helper.set = false; 224 | redir->helper.ptr = helper; 225 | 226 | set(redir->_src->flags, FW3_FAMILY_V4, FW3_FLAG_HELPER); 227 | } 228 | 229 | static bool 230 | check_redirect(struct fw3_state *state, struct fw3_redirect *redir, struct uci_element *e) 231 | { 232 | bool valid; 233 | 234 | if (!redir->enabled) 235 | return false; 236 | 237 | if (redir->src.invert) 238 | { 239 | warn_section("redirect", redir, e, "must not have an inverted source"); 240 | return false; 241 | } 242 | else if (redir->src.set && !redir->src.any && 243 | !(redir->_src = fw3_lookup_zone(state, redir->src.name))) 244 | { 245 | warn_section("redirect", redir, e, "refers to not existing zone '%s'", 246 | redir->src.name); 247 | return false; 248 | } 249 | else if (redir->dest.set && !redir->dest.any && 250 | !(redir->_dest = fw3_lookup_zone(state, redir->dest.name))) 251 | { 252 | warn_section("redirect", redir, e, "refers to not existing zone '%s'", 253 | redir->dest.name); 254 | return false; 255 | } 256 | else if (redir->ipset.set && state->disable_ipsets) 257 | { 258 | warn_section("redirect", redir, e, "skipped due to disabled ipset support"); 259 | return false; 260 | } 261 | else if (redir->ipset.set && 262 | !(redir->ipset.ptr = fw3_lookup_ipset(state, redir->ipset.name))) 263 | { 264 | warn_section("redirect", redir, e, "refers to unknown ipset '%s'", 265 | redir->ipset.name); 266 | return false; 267 | } 268 | else if (redir->helper.set && 269 | !(redir->helper.ptr = fw3_lookup_cthelper(state, redir->helper.name))) 270 | { 271 | warn_section("redirect", redir, e, "refers to unknown CT helper '%s'", 272 | redir->helper.name); 273 | return false; 274 | } 275 | 276 | if (!check_families(e, redir)) 277 | return false; 278 | 279 | if (redir->target == FW3_FLAG_UNSPEC) 280 | { 281 | warn_section("redirect", redir, e, "has no target specified, defaulting to DNAT"); 282 | redir->target = FW3_FLAG_DNAT; 283 | } 284 | else if (redir->target < FW3_FLAG_DNAT || redir->target > FW3_FLAG_SNAT) 285 | { 286 | warn_section("redirect", redir, e, "has invalid target specified, defaulting to DNAT"); 287 | redir->target = FW3_FLAG_DNAT; 288 | } 289 | 290 | valid = false; 291 | 292 | if (redir->target == FW3_FLAG_DNAT) 293 | { 294 | if (redir->src.any) 295 | warn_section("redirect", redir, e, "must not have source '*' for DNAT target"); 296 | else if (!redir->_src) 297 | warn_section("redirect", redir, e, "has no source specified"); 298 | else if (redir->helper.invert) 299 | warn_section("redirect", redir, e, "must not use a negated helper match"); 300 | else 301 | { 302 | set(redir->_src->flags, FW3_FAMILY_V4, redir->target); 303 | valid = true; 304 | 305 | if (!check_local(e, redir, state) && !redir->dest.set && 306 | resolve_dest(e, redir, state)) 307 | { 308 | warn_section("redirect", redir, e, 309 | "does not specify a destination, assuming '%s'", 310 | redir->dest.name); 311 | } 312 | 313 | if (redir->reflection && redir->_dest && redir->_src->masq) 314 | { 315 | set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_ACCEPT); 316 | set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_DNAT); 317 | set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT); 318 | } 319 | 320 | if (redir->helper.ptr) 321 | set(redir->_src->flags, FW3_FAMILY_V4, FW3_FLAG_HELPER); 322 | } 323 | } 324 | else 325 | { 326 | if (redir->dest.any) 327 | warn_section("redirect", redir, e, 328 | "must not have destination '*' for SNAT target"); 329 | else if (!redir->_dest) 330 | warn_section("redirect", redir, e, "has no destination specified"); 331 | else if (!redir->ip_dest.set) 332 | warn_section("redirect", redir, e, "has no src_dip option specified"); 333 | else if (!list_empty(&redir->mac_src)) 334 | warn_section("redirect", redir, e, "must not use 'src_mac' option for SNAT target"); 335 | else if (redir->helper.set) 336 | warn_section("redirect", redir, e, "must not use 'helper' option for SNAT target"); 337 | else 338 | { 339 | set(redir->_dest->flags, FW3_FAMILY_V4, redir->target); 340 | valid = true; 341 | } 342 | } 343 | 344 | if (list_empty(&redir->proto)) 345 | { 346 | warn_section("redirect", redir, e, "does not specify a protocol, assuming TCP+UDP"); 347 | fw3_parse_protocol(&redir->proto, "tcpudp", true); 348 | } 349 | 350 | if (!valid) 351 | return false; 352 | 353 | if (redir->target == FW3_FLAG_DNAT && !redir->port_redir.set) 354 | redir->port_redir = redir->port_dest; 355 | 356 | return true; 357 | } 358 | 359 | static struct fw3_redirect * 360 | fw3_alloc_redirect(struct fw3_state *state) 361 | { 362 | struct fw3_redirect *redir; 363 | 364 | redir = calloc(1, sizeof(*redir)); 365 | if (!redir) 366 | return NULL; 367 | 368 | INIT_LIST_HEAD(&redir->proto); 369 | INIT_LIST_HEAD(&redir->mac_src); 370 | INIT_LIST_HEAD(&redir->reflection_zones); 371 | 372 | redir->enabled = true; 373 | redir->reflection = true; 374 | 375 | list_add_tail(&redir->list, &state->redirects); 376 | 377 | return redir; 378 | } 379 | 380 | void 381 | fw3_load_redirects(struct fw3_state *state, struct uci_package *p, 382 | struct blob_attr *a) 383 | { 384 | struct uci_section *s; 385 | struct uci_element *e; 386 | struct fw3_redirect *redir; 387 | struct blob_attr *entry; 388 | unsigned rem; 389 | 390 | INIT_LIST_HEAD(&state->redirects); 391 | 392 | blob_for_each_attr(entry, a, rem) 393 | { 394 | const char *type; 395 | const char *name = "ubus redirect"; 396 | 397 | if (!fw3_attr_parse_name_type(entry, &name, &type)) 398 | continue; 399 | 400 | if (strcmp(type, "redirect")) 401 | continue; 402 | 403 | redir = fw3_alloc_redirect(state); 404 | if (!redir) 405 | continue; 406 | 407 | if (!fw3_parse_blob_options(redir, fw3_redirect_opts, entry, name)) 408 | { 409 | warn_section("redirect", redir, NULL, "skipped due to invalid options"); 410 | fw3_free_redirect(redir); 411 | continue; 412 | } 413 | 414 | if (!check_redirect(state, redir, NULL)) { 415 | fw3_free_redirect(redir); 416 | continue; 417 | } 418 | 419 | select_helper(state, redir); 420 | } 421 | 422 | uci_foreach_element(&p->sections, e) 423 | { 424 | s = uci_to_section(e); 425 | 426 | if (strcmp(s->type, "redirect")) 427 | continue; 428 | 429 | redir = fw3_alloc_redirect(state); 430 | if (!redir) 431 | continue; 432 | 433 | if (!fw3_parse_options(redir, fw3_redirect_opts, s)) 434 | { 435 | warn_elem(e, "skipped due to invalid options"); 436 | fw3_free_redirect(redir); 437 | continue; 438 | } 439 | 440 | if (!check_redirect(state, redir, e)) { 441 | fw3_free_redirect(redir); 442 | continue; 443 | } 444 | 445 | select_helper(state, redir); 446 | } 447 | } 448 | 449 | static void 450 | append_chain_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir) 451 | { 452 | if (redir->target == FW3_FLAG_DNAT) 453 | fw3_ipt_rule_append(r, "zone_%s_prerouting", redir->src.name); 454 | else 455 | fw3_ipt_rule_append(r, "zone_%s_postrouting", redir->dest.name); 456 | } 457 | 458 | static void 459 | set_redirect(struct fw3_ipt_rule *r, struct fw3_port *port) 460 | { 461 | char buf[sizeof("65535-65535")]; 462 | 463 | fw3_ipt_rule_target(r, "REDIRECT"); 464 | 465 | if (port && port->set) 466 | { 467 | if (port->port_min == port->port_max) 468 | snprintf(buf, sizeof(buf), "%u", port->port_min); 469 | else 470 | snprintf(buf, sizeof(buf), "%u-%u", port->port_min, port->port_max); 471 | 472 | fw3_ipt_rule_addarg(r, false, "--to-ports", buf); 473 | } 474 | } 475 | 476 | static void 477 | set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target, 478 | struct fw3_address *addr, struct fw3_port *port) 479 | { 480 | char buf[sizeof("255.255.255.255:65535-65535")] = {}; 481 | char ip[INET_ADDRSTRLEN], *p = buf; 482 | size_t rem = sizeof(buf); 483 | int len; 484 | 485 | if (addr && addr->set) 486 | { 487 | inet_ntop(AF_INET, &addr->address.v4, ip, sizeof(ip)); 488 | 489 | len = snprintf(p, rem, "%s", ip); 490 | 491 | if (len < 0 || len >= rem) 492 | return; 493 | 494 | rem -= len; 495 | p += len; 496 | } 497 | 498 | if (port && port->set) 499 | { 500 | if (port->port_min == port->port_max) 501 | snprintf(p, rem, ":%u", port->port_min); 502 | else 503 | snprintf(p, rem, ":%u-%u", port->port_min, port->port_max); 504 | } 505 | 506 | if (target == FW3_FLAG_DNAT) 507 | { 508 | fw3_ipt_rule_target(r, "DNAT"); 509 | fw3_ipt_rule_addarg(r, false, "--to-destination", buf); 510 | } 511 | else 512 | { 513 | fw3_ipt_rule_target(r, "SNAT"); 514 | fw3_ipt_rule_addarg(r, false, "--to-source", buf); 515 | } 516 | } 517 | 518 | static void 519 | set_target_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir) 520 | { 521 | if (redir->local) 522 | set_redirect(r, &redir->port_redir); 523 | else if (redir->target == FW3_FLAG_DNAT) 524 | set_snat_dnat(r, redir->target, &redir->ip_redir, &redir->port_redir); 525 | else 526 | set_snat_dnat(r, redir->target, &redir->ip_dest, &redir->port_dest); 527 | } 528 | 529 | static void 530 | set_comment(struct fw3_ipt_rule *r, const char *name, int num, const char *suffix) 531 | { 532 | if (name) 533 | { 534 | if (suffix) 535 | fw3_ipt_rule_comment(r, "%s (%s)", name, suffix); 536 | else 537 | fw3_ipt_rule_comment(r, name); 538 | } 539 | else 540 | { 541 | if (suffix) 542 | fw3_ipt_rule_comment(r, "@redirect[%u] (%s)", num, suffix); 543 | else 544 | fw3_ipt_rule_comment(r, "@redirect[%u]", num); 545 | } 546 | } 547 | 548 | static void 549 | print_redirect(struct fw3_ipt_handle *h, struct fw3_state *state, 550 | struct fw3_redirect *redir, int num, 551 | struct fw3_protocol *proto, struct fw3_mac *mac) 552 | { 553 | struct fw3_ipt_rule *r; 554 | struct fw3_address *src, *dst; 555 | struct fw3_port *spt, *dpt; 556 | 557 | switch (h->table) 558 | { 559 | case FW3_TABLE_NAT: 560 | src = &redir->ip_src; 561 | dst = &redir->ip_dest; 562 | spt = &redir->port_src; 563 | dpt = &redir->port_dest; 564 | 565 | if (redir->target == FW3_FLAG_SNAT) 566 | { 567 | dst = &redir->ip_redir; 568 | dpt = &redir->port_redir; 569 | } 570 | 571 | r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst); 572 | fw3_ipt_rule_sport_dport(r, spt, dpt); 573 | fw3_ipt_rule_mac(r, mac); 574 | fw3_ipt_rule_ipset(r, &redir->ipset); 575 | fw3_ipt_rule_helper(r, &redir->helper); 576 | fw3_ipt_rule_limit(r, &redir->limit); 577 | fw3_ipt_rule_time(r, &redir->time); 578 | fw3_ipt_rule_mark(r, &redir->mark); 579 | set_target_nat(r, redir); 580 | fw3_ipt_rule_extra(r, redir->extra); 581 | set_comment(r, redir->name, num, NULL); 582 | append_chain_nat(r, redir); 583 | break; 584 | 585 | case FW3_TABLE_RAW: 586 | if (redir->target == FW3_FLAG_DNAT && redir->helper.ptr) 587 | { 588 | if (!fw3_cthelper_check_proto(redir->helper.ptr, proto)) 589 | { 590 | info(" ! Skipping protocol %s since helper '%s' does not support it", 591 | fw3_protoname(proto), redir->helper.ptr->name); 592 | return; 593 | } 594 | 595 | if (!redir->helper.set) 596 | info(" - Auto-selected conntrack helper '%s' based on proto/port", 597 | redir->helper.ptr->name); 598 | 599 | r = fw3_ipt_rule_create(h, proto, NULL, NULL, &redir->ip_src, &redir->ip_redir); 600 | fw3_ipt_rule_sport_dport(r, &redir->port_src, &redir->port_redir); 601 | fw3_ipt_rule_mac(r, mac); 602 | fw3_ipt_rule_ipset(r, &redir->ipset); 603 | fw3_ipt_rule_limit(r, &redir->limit); 604 | fw3_ipt_rule_time(r, &redir->time); 605 | fw3_ipt_rule_mark(r, &redir->mark); 606 | fw3_ipt_rule_addarg(r, false, "-m", "conntrack"); 607 | fw3_ipt_rule_addarg(r, false, "--ctstate", "DNAT"); 608 | fw3_ipt_rule_target(r, "CT"); 609 | fw3_ipt_rule_addarg(r, false, "--helper", redir->helper.ptr->name); 610 | set_comment(r, redir->name, num, "CT helper"); 611 | fw3_ipt_rule_append(r, "zone_%s_helper", redir->_src->name); 612 | } 613 | break; 614 | 615 | default: 616 | break; 617 | } 618 | } 619 | 620 | static void 621 | print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state, 622 | struct fw3_redirect *redir, int num, 623 | struct fw3_protocol *proto, struct fw3_address *ra, 624 | struct fw3_address *ia, struct fw3_address *ea, struct fw3_device *rz) 625 | { 626 | struct fw3_ipt_rule *r; 627 | 628 | switch (h->table) 629 | { 630 | case FW3_TABLE_NAT: 631 | r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, ea); 632 | fw3_ipt_rule_sport_dport(r, NULL, &redir->port_dest); 633 | fw3_ipt_rule_limit(r, &redir->limit); 634 | fw3_ipt_rule_time(r, &redir->time); 635 | set_comment(r, redir->name, num, "reflection"); 636 | set_snat_dnat(r, FW3_FLAG_DNAT, &redir->ip_redir, &redir->port_redir); 637 | fw3_ipt_rule_replace(r, "zone_%s_prerouting", rz->name); 638 | 639 | r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, &redir->ip_redir); 640 | fw3_ipt_rule_sport_dport(r, NULL, &redir->port_redir); 641 | fw3_ipt_rule_limit(r, &redir->limit); 642 | fw3_ipt_rule_time(r, &redir->time); 643 | set_comment(r, redir->name, num, "reflection"); 644 | set_snat_dnat(r, FW3_FLAG_SNAT, ra, NULL); 645 | fw3_ipt_rule_replace(r, "zone_%s_postrouting", rz->name); 646 | break; 647 | 648 | default: 649 | break; 650 | } 651 | } 652 | 653 | static void 654 | expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state, 655 | struct fw3_redirect *redir, int num) 656 | { 657 | struct list_head *ext_addrs, *int_addrs; 658 | struct fw3_address *ext_addr, *int_addr, ref_addr; 659 | struct fw3_protocol *proto; 660 | struct fw3_mac *mac; 661 | struct fw3_device *reflection_zone; 662 | struct fw3_zone *zone; 663 | 664 | if (redir->name) 665 | info(" * Redirect '%s'", redir->name); 666 | else 667 | info(" * Redirect #%u", num); 668 | 669 | if (!fw3_is_family(redir->_src, handle->family) || 670 | !fw3_is_family(redir->_dest, handle->family)) 671 | { 672 | info(" ! Skipping due to different family of zone"); 673 | return; 674 | } 675 | 676 | if (!fw3_is_family(&redir->ip_src, handle->family) || 677 | !fw3_is_family(&redir->ip_dest, handle->family) || 678 | !fw3_is_family(&redir->ip_redir, handle->family)) 679 | { 680 | if (!redir->ip_src.resolved || 681 | !redir->ip_dest.resolved || 682 | !redir->ip_redir.resolved) 683 | info(" ! Skipping due to different family of ip address"); 684 | 685 | return; 686 | } 687 | 688 | if (redir->ipset.ptr) 689 | { 690 | if (!fw3_is_family(redir->ipset.ptr, handle->family)) 691 | { 692 | info(" ! Skipping due to different family in ipset"); 693 | return; 694 | } 695 | 696 | if (!fw3_check_ipset(redir->ipset.ptr)) 697 | { 698 | info(" ! Skipping due to missing ipset '%s'", 699 | redir->ipset.ptr->external ? 700 | redir->ipset.ptr->external : redir->ipset.ptr->name); 701 | return; 702 | } 703 | 704 | set(redir->ipset.ptr->flags, handle->family, handle->family); 705 | } 706 | 707 | fw3_foreach(proto, &redir->proto) 708 | fw3_foreach(mac, &redir->mac_src) 709 | print_redirect(handle, state, redir, num, proto, mac); 710 | 711 | /* reflection rules */ 712 | if (redir->target != FW3_FLAG_DNAT || !redir->reflection || redir->local) 713 | return; 714 | 715 | if (!redir->_dest || !redir->_src->masq) 716 | return; 717 | 718 | ext_addrs = fw3_resolve_zone_addresses(redir->_src, &redir->ip_dest); 719 | if (!ext_addrs) 720 | return; 721 | 722 | list_for_each_entry(ext_addr, ext_addrs, list) 723 | { 724 | if (!fw3_is_family(ext_addr, handle->family)) 725 | continue; 726 | 727 | for (reflection_zone = list_empty(&redir->reflection_zones) 728 | ? &redir->dest 729 | : list_first_entry(&redir->reflection_zones, struct fw3_device, list); 730 | list_empty(&redir->reflection_zones) 731 | ? (reflection_zone == &redir->dest) 732 | : (&reflection_zone->list != &redir->reflection_zones); 733 | reflection_zone = list_empty(&redir->reflection_zones) 734 | ? NULL 735 | : list_entry(reflection_zone->list.next, struct fw3_device, list)) 736 | { 737 | zone = fw3_lookup_zone(state, reflection_zone->name); 738 | 739 | if (!zone) 740 | continue; 741 | 742 | int_addrs = fw3_resolve_zone_addresses(zone, NULL); 743 | if (!int_addrs) 744 | continue; 745 | 746 | list_for_each_entry(int_addr, int_addrs, list) 747 | { 748 | if (!fw3_is_family(int_addr, handle->family)) 749 | continue; 750 | 751 | fw3_foreach(proto, &redir->proto) 752 | { 753 | if (!proto) 754 | continue; 755 | 756 | if (redir->reflection_src == FW3_REFLECTION_INTERNAL) 757 | ref_addr = *int_addr; 758 | else 759 | ref_addr = *ext_addr; 760 | 761 | ref_addr.mask.v4.s_addr = 0xFFFFFFFF; 762 | ext_addr->mask.v4.s_addr = 0xFFFFFFFF; 763 | 764 | print_reflection(handle, state, redir, num, proto, 765 | &ref_addr, int_addr, ext_addr, reflection_zone); 766 | } 767 | } 768 | 769 | fw3_free_list(int_addrs); 770 | } 771 | } 772 | 773 | fw3_free_list(ext_addrs); 774 | } 775 | 776 | void 777 | fw3_print_redirects(struct fw3_ipt_handle *handle, struct fw3_state *state) 778 | { 779 | int num = 0; 780 | struct fw3_redirect *redir; 781 | 782 | if (handle->family == FW3_FAMILY_V6) 783 | return; 784 | 785 | if (handle->table != FW3_TABLE_FILTER && 786 | handle->table != FW3_TABLE_NAT && 787 | handle->table != FW3_TABLE_RAW) 788 | return; 789 | 790 | list_for_each_entry(redir, &state->redirects, list) 791 | { 792 | if (handle->table == FW3_TABLE_RAW && !redir->helper.ptr) 793 | continue; 794 | 795 | expand_redirect(handle, state, redir, num++); 796 | } 797 | } 798 | --------------------------------------------------------------------------------