diff --git a/.clang-format b/.clang-format old mode 100755 new mode 100644 diff --git a/ReadMe.md b/ReadMe.md index 85512b9..271b0d6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -515,8 +515,8 @@ entware|ipkg update
ipkg install smartdns|软件源路径:https://bin.entw | 键名 | 功能说明 | 默认值 | 可用值/要求 | 举例 | | :--- | :--- | :--- | :--- | :--- | | server-name | DNS 服务器名称 | 操作系统主机名 / smartdns | 符合主机名规格的字符串 | server-name smartdns | -| bind | DNS 监听端口号 | [::]:53 | 可绑定多个端口。
IP:PORT: 服务器 IP:端口号
[-group]: 请求时使用的 DNS 服务器组
[-no-rule-addr]:跳过 address 规则
[-no-rule-nameserver]:跳过 Nameserver 规则
[-no-rule-ipset]:跳过 ipset 规则
[-no-rule-soa]:跳过 SOA(#) 规则
[-no-dualstack-selection]:停用双栈测速
[-no-speed-check]:停用测速
[-no-cache]:停止缓存 | bind :53 | -| bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。
IP:PORT: 服务器 IP:端口号
[-group]: 请求时使用的 DNS 服务器组
[-no-rule-addr]:跳过 address 规则
[-no-rule-nameserver]:跳过 nameserver 规则
[-no-rule-ipset]:跳过 ipset 规则。
[-no-rule-soa]:跳过 SOA(#) 规则
[-no-dualstack-selection]:停用双栈测速
[-no-speed-check]:停用测速
[-no-cache]:停止缓存 | bind-tcp :53 | +| bind | DNS 监听端口号 | [::]:53 | 可绑定多个端口。
IP:PORT: 服务器 IP:端口号
[-group]: 请求时使用的 DNS 服务器组
[-no-rule-addr]:跳过 address 规则
[-no-rule-nameserver]:跳过 Nameserver 规则
[-no-rule-ipset]:跳过 ipset 和 nftset 规则
[-no-rule-soa]:跳过 SOA(#) 规则
[-no-dualstack-selection]:停用双栈测速
[-no-speed-check]:停用测速
[-no-cache]:停止缓存 | bind :53 | +| bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。
IP:PORT: 服务器 IP:端口号
[-group]: 请求时使用的 DNS 服务器组
[-no-rule-addr]:跳过 address 规则
[-no-rule-nameserver]:跳过 nameserver 规则
[-no-rule-ipset]:跳过 ipset 和 nftset 规则。
[-no-rule-soa]:跳过 SOA(#) 规则
[-no-dualstack-selection]:停用双栈测速
[-no-speed-check]:停用测速
[-no-cache]:停止缓存 | bind-tcp :53 | | cache-size | 域名结果缓存个数 | 512 | 大于等于 0 的数字 | cache-size 512 | | cache-persist | 是否持久化缓存 | 自动。
当 cache-file 所在的位置有超过 128 MB 的可用空间时启用,否则禁用。 | [yes\|no] | cache-persist yes | | cache-file | 缓存持久化文件路径 | /tmp/smartdns.cache | 合法路径字符串 | cache-file /tmp/smartdns.cache | @@ -546,8 +546,10 @@ entware|ipkg update
ipkg install smartdns|软件源路径:https://bin.entw | nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office | | ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]],-表示忽略 | ipset /www.example.com/#4:dns4,#6:- | | ipset-timeout | 设置 ipset 超时功能启用 | 自动 | [yes] | ipset-timeout yes | -| domain-rules | 设置域名规则 | 无 | domain-rules /domain/ [-rules...]
[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置
[-a\|-address]:参考 address 配置
[-n\|-nameserver]:参考 nameserver 配置
[-p\|-ipset]:参考ipset配置
[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection | domain-rules /www.example.com/ -speed-check-mode none | -| domain-set | 设置域名集合 | 无 | domain-set [options...]
[-n\|-name]:域名集合名称
[-t\|-type]:域名集合类型,当前仅支持list,格式为域名列表,一行一个域名。
[-f\|-file]:域名集合文件路径。
选项需要配合address, nameserver, ipset等需要指定域名的地方使用,使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list
address /domain-set:set/1.2.4.8 | +| nftset | 域名 nftset | 无 | nftset /domain/#[4\|6]:[family#nftable#nftset\|-][,#[4\|6]:[family#nftable#nftset\|-]]],-表示忽略;ipv4 地址的 family 只支持 inet 和 ip;ipv6 地址的 family 只支持 inet 和 ip6;由于 nft 限制,两种地址只能分开存放于两个 set 中。| nftset /www.example.com/#4:inet#mytab#dns4,#6:- | +| nftset-timeout | 设置 nftset 超时功能启用 | 自动 | [yes] | nftset-timeout yes | +| domain-rules | 设置域名规则 | 无 | domain-rules /domain/ [-rules...]
[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置
[-a\|-address]:参考 address 配置
[-n\|-nameserver]:参考 nameserver 配置
[-p\|-ipset]:参考ipset配置
[-s\|-nftset]:参考nftset配置
[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection | domain-rules /www.example.com/ -speed-check-mode none | +| domain-set | 设置域名集合 | 无 | domain-set [options...]
[-n\|-name]:域名集合名称
[-t\|-type]:域名集合类型,当前仅支持list,格式为域名列表,一行一个域名。
[-f\|-file]:域名集合文件路径。
选项需要配合address, nameserver, ipset, nftset等需要指定域名的地方使用,使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list
address /domain-set:set/1.2.4.8 | | bogus-nxdomain | 假冒 IP 地址过滤 | 无 | [ip/subnet],可重复 | bogus-nxdomain 1.2.3.4/16 | | ignore-ip | 忽略 IP 地址 | 无 | [ip/subnet],可重复 | ignore-ip 1.2.3.4/16 | | whitelist-ip | 白名单 IP 地址 | 无 | [ip/subnet],可重复 | whitelist-ip 1.2.3.4/16 | diff --git a/ReadMe_en.md b/ReadMe_en.md old mode 100755 new mode 100644 index fff4be2..b7cca64 --- a/ReadMe_en.md +++ b/ReadMe_en.md @@ -474,8 +474,8 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |parameter|Parameter function|Default value|Value type|Example| |--|--|--|--|--| |server-name|DNS name|host name/smartdns|any string like hosname|server-name smartdns -|bind|DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset rule.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind :53 -|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset rule.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind-tcp :53 +|bind|DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset or nftset rules.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind :53 +|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports
`IP:PORT`: server IP, port number.
`[-group]`: The DNS server group used when requesting.
`[-no-rule-addr]`: Skip the address rule.
`[-no-rule-nameserver]`: Skip the Nameserver rule.
`[-no-rule-ipset]`: Skip the Ipset or nftset rules.
`[-no-rule-soa]`: Skip address SOA(#) rules.
`[-no-dualstack-selection]`: Disable dualstack ip selection.
`[-no-speed-check]`: Disable speed measurement.
`[-no-cache]`: stop caching |bind-tcp :53 |cache-size|Domain name result cache number|512|integer|cache-size 512 |cache-persist|enable persist cache|Auto: Enabled if the location of `cache-file` has more than 128MB of free space.|[yes\|no]|cache-persist yes |cache-file|cache persist file|/tmp/smartdns.cache|路径|cache-file /tmp/smartdns.cache @@ -505,8 +505,10 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office |ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:- |ipset-timeout|ipset timeout enable|auto|[yes]|ipset-timeout yes -|domain-rules|set domain rules|None|domain-rules /domain/ [-rules...]
`[-c\|-speed-check-mode]`: set speed check mode,same as parameter `speed-check-mode`
`[-a\|-address]`: same as parameter `address`
`[-n\|-nameserver]`: same as parameter `nameserver`
`[-p\|-ipset]`: same as parameter `ipset`
`[-d\|-dualstack-ip-selection]`: same as parameter `dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none -| domain-set | collection of domains|None| domain-set [options...]
[-n\|-name]:name of set
[-t\|-type] [list]: set type, only support list, one domain per line
[-f\|-file]:file path of domain set
used with address, nameserver, ipset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list
address /domain-set:set/1.2.4.8 | +|nftset|Domain nftset|None|nftset /domain/#[4\|6]:[family#nftable#nftset\|-][,#[4\|6]:[family#nftable#nftset\|-]]], `-` to ignore; the valid families are inet and ip for ipv4 addresses while the valid ones are inet and ip6 for ipv6 addresses; due to the limitation of nft, two types of addresses have to be stored in two sets|nftset /www.example.com/#4:inet#mytab#dns4,#6:- +|nftset-timeout|nftset timeout enable|auto|[yes]|nftset-timeout yes +|domain-rules|set domain rules|None|domain-rules /domain/ [-rules...]
`[-c\|-speed-check-mode]`: set speed check mode,same as parameter `speed-check-mode`
`[-a\|-address]`: same as parameter `address`
`[-n\|-nameserver]`: same as parameter `nameserver`
`[-p\|-ipset]`: same as parameter `nftset`
`[-s\|-nftset]`: same as parameter `nftset`
`[-d\|-dualstack-ip-selection]`: same as parameter `dualstack-ip-selection`|domain-rules /www.example.com/ -speed-check-mode none +| domain-set | collection of domains|None| domain-set [options...]
[-n\|-name]:name of set
[-t\|-type] [list]: set type, only support list, one domain per line
[-f\|-file]:file path of domain set
used with address, nameserver, ipset, nftset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list
address /domain-set:set/1.2.4.8 | |bogus-nxdomain|bogus IP address|None|[IP/subnet], Repeatable| bogus-nxdomain 1.2.3.4/16 |ignore-ip|ignore ip address|None|[ip/subnet], Repeatable| ignore-ip 1.2.3.4/16 |whitelist-ip|ip whitelist|None|[ip/subnet], Repeatable,When the filtering server responds IPs in the IP whitelist, only result in whitelist will be accepted| whitelist-ip 1.2.3.4/16 diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 64fdac3..6ae7405 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -26,7 +26,7 @@ # -group: set domain request to use the appropriate server group. # -no-rule-addr: skip address rule. # -no-rule-nameserver: skip nameserver rule. -# -no-rule-ipset: skip ipset rule. +# -no-rule-ipset: skip ipset rule or nftset rule. # -no-speed-check: do not check speed. # -no-cache: skip cache. # -no-rule-soa: Skip address SOA(#) rules. diff --git a/package/build-pkg.sh b/package/build-pkg.sh index f3e6b36..a21312d 100755 --- a/package/build-pkg.sh +++ b/package/build-pkg.sh @@ -16,6 +16,7 @@ showhelp() echo " --platform [luci|luci-compat|debian|openwrt|optware|linux] build for platform. " echo " --arch [all|armhf|arm64|x86-64|...] build for architecture, e.g. " echo " --cross-tool [cross-tool] cross compiler, e.g. mips-openwrt-linux-" + echo " --with-nftables build with nftables support" echo "" echo "Advance Options:" echo " --static static link smartdns" @@ -81,7 +82,7 @@ build() main() { - OPTS=`getopt -o o:h --long arch:,filearch:,ver:,platform:,cross-tool:,static,only-package,outputdir: \ + OPTS=`getopt -o o:h --long arch:,filearch:,ver:,platform:,cross-tool:,with-nftables,static,only-package,outputdir: \ -n "" -- "$@"` if [ "$#" -le "1" ]; then @@ -108,6 +109,9 @@ main() --cross-tool) CROSS_TOOL="$2" shift 2;; + --with-nftables) + MAKE_ARGS="WITH_NFTSET=1" + shift 1;; --static) export STATIC="yes" shift 1;; diff --git a/src/Makefile b/src/Makefile index 25dce39..e3ece41 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,10 +38,15 @@ else override LDFLAGS += -lssl -lcrypto -lpthread -ldl endif +ifdef WITH_NFTSET +override CFLAGS += -DWITH_NFTSET +override LDFLAGS += -lnftables +endif + .PHONY: all clean all: $(BIN) - + $(BIN) : $(OBJS) $(CC) $(OBJS) -o $@ $(LDFLAGS) diff --git a/src/dns_conf.c b/src/dns_conf.c index 3dc8927..1bcc37b 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -40,6 +40,11 @@ struct dns_ipset_table { }; static struct dns_ipset_table dns_ipset_table; +struct dns_nftset_table { + DECLARE_HASHTABLE(nftset, 8); +}; +static struct dns_nftset_table dns_nftset_table; + struct dns_qtype_soa_table dns_qtype_soa_table; struct dns_domain_set_rule_table dns_domain_set_rule_table; @@ -133,6 +138,7 @@ int dns_conf_local_ttl; int dns_conf_force_AAAA_SOA; int dns_conf_force_no_cname; int dns_conf_ipset_timeout_enable; +int dns_conf_nftset_timeout_enable; char dns_conf_user[DNS_CONF_USRNAME_LEN]; @@ -170,6 +176,10 @@ static void *_new_dns_rule(enum domain_rule domain_rule) case DOMAIN_RULE_IPSET_IPV6: size = sizeof(struct dns_ipset_rule); break; + case DOMAIN_RULE_NFTSET_IP: + case DOMAIN_RULE_NFTSET_IP6: + size = sizeof(struct dns_nftset_rule); + break; case DOMAIN_RULE_NAMESERVER: size = sizeof(struct dns_nameserver_rule); break; @@ -853,6 +863,165 @@ errout: return 0; } +static void _config_nftset_table_destroy(void) +{ + struct dns_nftset_name *nftset = NULL; + struct hlist_node *tmp = NULL; + unsigned long i = 0; + + hash_for_each_safe(dns_nftset_table.nftset, i, tmp, nftset, node) + { + hlist_del_init(&nftset->node); + free(nftset); + } +} + +static const struct dns_nftset_name *_dns_conf_get_nftable(const char *familyname, const char *tablename, + const char *setname) +{ + uint32_t key = 0; + struct dns_nftset_name *nftset_name = NULL; + + if (familyname == NULL || tablename == NULL || setname == NULL) { + return NULL; + } + + const char *hasher[4] = {familyname, tablename, setname, NULL}; + + key = hash_string_array(hasher); + hash_for_each_possible(dns_nftset_table.nftset, nftset_name, node, key) + { + if (strncmp(nftset_name->nftfamilyname, familyname, DNS_MAX_NFTSET_FAMILYLEN) == 0 && + strncmp(nftset_name->nfttablename, tablename, DNS_MAX_NFTSET_NAMELEN) == 0 && + strncmp(nftset_name->nftsetname, setname, DNS_MAX_NFTSET_NAMELEN) == 0) { + return nftset_name; + } + } + + nftset_name = malloc(sizeof(*nftset_name)); + if (nftset_name == NULL) { + goto errout; + } + + safe_strncpy(nftset_name->nftfamilyname, familyname, DNS_MAX_NFTSET_FAMILYLEN); + safe_strncpy(nftset_name->nfttablename, tablename, DNS_MAX_NFTSET_NAMELEN); + safe_strncpy(nftset_name->nftsetname, setname, DNS_MAX_NFTSET_NAMELEN); + hash_add(dns_nftset_table.nftset, &nftset_name->node, key); + + return nftset_name; +errout: + if (nftset_name) { + free(nftset_name); + } + + return NULL; +} + +static int _conf_domain_rule_nftset(char *domain, const char *nftsetname) +{ + struct dns_nftset_rule *nftset_rule = NULL; + const struct dns_nftset_name *nftset = NULL; + char *copied_name = NULL; + enum domain_rule type = 0; + int ignore_flag = 0; + char *setname = NULL; + char *tablename = NULL; + + copied_name = strdup(nftsetname); + + if (copied_name == NULL) { + goto errout; + } + + for (char *tok = strtok(copied_name, ","); tok; tok = strtok(NULL, ",")) { + if (tok[0] == '#') { + if (strncmp(tok, "#6:inet#", 8U) == 0 || strncmp(tok, "#6:ip6#", 7U) == 0) { + type = DOMAIN_RULE_NFTSET_IP6; + ignore_flag = DOMAIN_FLAG_NFTSET_IP6_IGN; + } else if (strncmp(tok, "#4:inet#", 4U) == 0 || strncmp(tok, "#4:ip#", 6U) == 0) { + type = DOMAIN_RULE_NFTSET_IP; + ignore_flag = DOMAIN_FLAG_NFTSET_IP_IGN; + } else { + goto errout; + } + tok += 3; + } else { + goto errout; + } + + if (strncmp(tok, "-", 1U) == 0) { + _config_domain_rule_flag_set(domain, ignore_flag, 0); + continue; + } + + tablename = strpbrk(tok, "#"); + if (tablename == NULL) { + goto errout; + } + *tablename++ = '\0'; + setname = strpbrk(tablename, "#"); + if (setname == NULL) { + goto errout; + } + *setname++ = '\0'; + + /* new ipset domain */ + nftset = _dns_conf_get_nftable(tok, tablename, setname); + if (nftset == NULL) { + goto errout; + } + + nftset_rule = _new_dns_rule(type); + if (nftset_rule == NULL) { + goto errout; + } + + nftset_rule->nfttablename = nftset->nfttablename; + nftset_rule->nftsetname = nftset->nftsetname; + nftset_rule->familyname = nftset->nftfamilyname; + + if (_config_domain_rule_add(domain, type, nftset_rule) != 0) { + goto errout; + } + _dns_rule_put(&nftset_rule->head); + } + + goto clear; + +errout: + tlog(TLOG_ERROR, "add nftset %s failed", nftsetname); + + if (nftset_rule) { + _dns_rule_put(&nftset_rule->head); + } + +clear: + if (copied_name) { + free(copied_name); + } + + return 0; +} + +static int _config_nftset(void *data, int argc, char *argv[]) +{ + char domain[DNS_MAX_CONF_CNAME_LEN]; + char *value = argv[1]; + + if (argc <= 1) { + goto errout; + } + + if (_get_domain(value, domain, DNS_MAX_CONF_CNAME_LEN, &value) != 0) { + goto errout; + } + + return _conf_domain_rule_nftset(domain, value); +errout: + tlog(TLOG_ERROR, "add nftset %s failed", value); + return 0; +} + static int _conf_domain_rule_address(char *domain, const char *domain_address) { struct dns_rule_address_IPV4 *address_ipv4 = NULL; @@ -1665,6 +1834,7 @@ static int _conf_domain_rules(void *data, int argc, char *argv[]) {"speed-check-mode", required_argument, NULL, 'c'}, {"address", required_argument, NULL, 'a'}, {"ipset", required_argument, NULL, 'p'}, + {"nftset", required_argument, NULL, 's'}, {"nameserver", required_argument, NULL, 'n'}, {"dualstack-ip-selection", required_argument, NULL, 'd'}, {NULL, no_argument, NULL, 0} @@ -1750,6 +1920,19 @@ static int _conf_domain_rules(void *data, int argc, char *argv[]) break; } + case 's': { + const char *nftsetname = optarg; + if (nftsetname == NULL) { + goto errout; + } + + if (_conf_domain_rule_nftset(domain, nftsetname) != 0) { + tlog(TLOG_ERROR, "add nftset rule failed."); + goto errout; + } + + break; + } default: break; } @@ -2178,6 +2361,8 @@ static struct config_item _config_item[] = { CONF_CUSTOM("address", _config_address, NULL), CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable), CONF_CUSTOM("ipset", _config_ipset, NULL), + CONF_YESNO("nftset-timeout", &dns_conf_nftset_timeout_enable), + CONF_CUSTOM("nftset", _config_nftset, NULL), CONF_CUSTOM("speed-check-mode", _config_speed_check_mode, NULL), CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600), CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX), @@ -2384,6 +2569,7 @@ static int _dns_server_load_conf_init(void) art_tree_init(&dns_conf_domain_rule); hash_init(dns_ipset_table.ipset); + hash_init(dns_nftset_table.nftset); hash_init(dns_qtype_soa_table.qtype); hash_init(dns_group_table.group); hash_init(dns_hosts_table.hosts); @@ -2400,6 +2586,7 @@ void dns_server_load_exit(void) Destroy_Radix(dns_conf_address_rule.ipv4, _config_address_destroy, NULL); Destroy_Radix(dns_conf_address_rule.ipv6, _config_address_destroy, NULL); _config_ipset_table_destroy(); + _config_nftset_table_destroy(); _config_group_table_destroy(); _config_ptr_table_destroy(); _config_host_table_destroy(); diff --git a/src/dns_conf.h b/src/dns_conf.h index 50550d4..7fbc190 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -38,6 +38,8 @@ extern "C" { #define DNS_MAX_SERVER_NAME_LEN 128 #define DNS_MAX_PTR_LEN 128 #define DNS_MAX_IPSET_NAMELEN 32 +#define DNS_MAX_NFTSET_FAMILYLEN 8 +#define DNS_MAX_NFTSET_NAMELEN 256 #define DNS_GROUP_NAME_LEN 32 #define DNS_NAX_GROUP_NUMBER 16 #define DNS_MAX_IPLEN 64 @@ -62,6 +64,8 @@ enum domain_rule { DOMAIN_RULE_IPSET, DOMAIN_RULE_IPSET_IPV4, DOMAIN_RULE_IPSET_IPV6, + DOMAIN_RULE_NFTSET_IP, + DOMAIN_RULE_NFTSET_IP6, DOMAIN_RULE_NAMESERVER, DOMAIN_RULE_CHECKSPEED, DOMAIN_RULE_MAX, @@ -90,6 +94,9 @@ typedef enum { #define DOMAIN_FLAG_NAMESERVER_IGNORE (1 << 9) #define DOMAIN_FLAG_DUALSTACK_SELECT (1 << 10) #define DOMAIN_FLAG_SMARTDNS_DOMAIN (1 << 11) +#define DOMAIN_FLAG_NFTSET_INET_IGN (1 << 12) +#define DOMAIN_FLAG_NFTSET_IP_IGN (1 << 13) +#define DOMAIN_FLAG_NFTSET_IP6_IGN (1 << 14) #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0) @@ -135,6 +142,20 @@ struct dns_ipset_rule { const char *ipsetname; }; +struct dns_nftset_name { + struct hlist_node node; + char nftfamilyname[DNS_MAX_NFTSET_FAMILYLEN]; + char nfttablename[DNS_MAX_NFTSET_NAMELEN]; + char nftsetname[DNS_MAX_NFTSET_NAMELEN]; +}; + +struct dns_nftset_rule { + struct dns_rule head; + const char *familyname; + const char *nfttablename; + const char *nftsetname; +}; + struct dns_domain_rule { struct dns_rule head; struct dns_rule *rules[DOMAIN_RULE_MAX]; @@ -366,6 +387,7 @@ extern int dns_conf_rr_ttl_min; extern int dns_conf_rr_ttl_max; extern int dns_conf_force_AAAA_SOA; extern int dns_conf_ipset_timeout_enable; +extern int dns_conf_nftset_timeout_enable; extern int dns_conf_local_ttl; extern int dns_conf_force_no_cname; diff --git a/src/dns_server.c b/src/dns_server.c index aa951b8..a0ca739 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -1298,7 +1298,7 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context) return 0; } -static int _dns_server_setup_ipset_packet(struct dns_server_post_context *context) +static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context *context) { int ttl = 0; struct dns_request *request = context->request; @@ -1311,6 +1311,8 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex struct dns_ipset_rule *ipset_rule = NULL; struct dns_ipset_rule *ipset_rule_v4 = NULL; struct dns_ipset_rule *ipset_rule_v6 = NULL; + struct dns_nftset_rule *nftset_ip = NULL; + struct dns_nftset_rule *nftset_ip6 = NULL; struct dns_rule_flags *rule_flags = NULL; if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_IPSET) == 0) { @@ -1336,8 +1338,14 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV6_IGN) == 0) { ipset_rule_v6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV6); } + if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP_IGN) == 0) { + nftset_ip = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP); + } + if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_NFTSET_IP6_IGN) == 0) { + nftset_ip6 = _dns_server_get_dns_rule(request, DOMAIN_RULE_NFTSET_IP6); + } - if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6)) { + if (!(ipset_rule || ipset_rule_v4 || ipset_rule_v6 || nftset_ip || nftset_ip6)) { return 0; } @@ -1354,14 +1362,23 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule; - if (rule == NULL) { - break; + if (rule != NULL) { + /* add IPV4 to ipset */ + ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ip_ttl * 2); + tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, + rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); } - /* add IPV4 to ipset */ - ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ip_ttl * 2); - tlog(TLOG_DEBUG, "IPSET-MATCH: domain: %s, ipset: %s, IP: %d.%d.%d.%d", request->domain, - rule->ipsetname, addr[0], addr[1], addr[2], addr[3]); +#ifdef WITH_NFTSET + if (nftset_ip != NULL) { + /* add IPV4 to ipset */ + nftset_add(nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr, + DNS_RR_A_LEN, request->ip_ttl * 2); + tlog(TLOG_DEBUG, "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: %d.%d.%d.%d", request->domain, + nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr[0], addr[1], + addr[2], addr[3]); + } +#endif } break; case DNS_T_AAAA: { unsigned char addr[16]; @@ -1372,16 +1389,28 @@ static int _dns_server_setup_ipset_packet(struct dns_server_post_context *contex dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule; - if (rule == NULL) { - break; + if (rule != NULL) { + ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ip_ttl * 2); + tlog(TLOG_DEBUG, + "IPSET-MATCH: domain: %s, ipset: %s, IP: " + "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", + request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], + addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], + addr[15]); } - - ipset_add(rule->ipsetname, addr, DNS_RR_AAAA_LEN, request->ip_ttl * 2); - tlog(TLOG_DEBUG, - "IPSET-MATCH: domain: %s, ipset: %s, IP: " - "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", - request->domain, rule->ipsetname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], - addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); +#ifdef WITH_NFTSET + if (nftset_ip6 != NULL) { + /* add IPV6 to ipset */ + nftset_add(nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, addr, + DNS_RR_AAAA_LEN, request->ip_ttl * 2); + tlog(TLOG_DEBUG, + "NFTSET-MATCH: domain: %s, nftset: %s %s %s, IP: " + "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", + request->domain, nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], + addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); + } +#endif } break; default: break; @@ -1420,7 +1449,7 @@ static int _dns_request_post(struct dns_server_post_context *context) } /* setup ipset */ - _dns_server_setup_ipset_packet(context); + _dns_server_setup_ipset_nftset_packet(context); if (context->do_reply == 0) { return 0; @@ -2769,7 +2798,7 @@ static int _dns_server_reply_passthrouth(struct dns_server_post_context *context _dns_cache_reply_packet(context); - if (_dns_server_setup_ipset_packet(context) != 0) { + if (_dns_server_setup_ipset_nftset_packet(context) != 0) { tlog(TLOG_DEBUG, "setup ipset failed."); } diff --git a/src/include/hash.h b/src/include/hash.h index 67c49eb..08036c7 100644 --- a/src/include/hash.h +++ b/src/include/hash.h @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - #ifndef _GENERIC_HASH_H #define _GENERIC_HASH_H @@ -30,7 +29,7 @@ #endif #ifndef BITS_PER_LONG -# define BITS_PER_LONG __WORDSIZE +#define BITS_PER_LONG __WORDSIZE #endif /* @@ -39,14 +38,12 @@ * more efficiently than using fls() and fls64() * - the arch is not required to handle n==0 if implementing the fallback */ -static inline __attribute__((const)) -int __ilog2_u32(uint32_t n) +static inline __attribute__((const)) int __ilog2_u32(uint32_t n) { return fls(n) - 1; } -static inline __attribute__((const)) -int __ilog2_u64(uint64_t n) +static inline __attribute__((const)) int __ilog2_u64(uint64_t n) { return fls64(n) - 1; } @@ -61,78 +58,73 @@ int __ilog2_u64(uint64_t n) * * selects the appropriately-sized optimised version depending on sizeof(n) */ -#define ilog2(n) \ -( \ - __builtin_constant_p(n) ? ( \ - (n) < 2 ? 0 : \ - (n) & (1ULL << 63) ? 63 : \ - (n) & (1ULL << 62) ? 62 : \ - (n) & (1ULL << 61) ? 61 : \ - (n) & (1ULL << 60) ? 60 : \ - (n) & (1ULL << 59) ? 59 : \ - (n) & (1ULL << 58) ? 58 : \ - (n) & (1ULL << 57) ? 57 : \ - (n) & (1ULL << 56) ? 56 : \ - (n) & (1ULL << 55) ? 55 : \ - (n) & (1ULL << 54) ? 54 : \ - (n) & (1ULL << 53) ? 53 : \ - (n) & (1ULL << 52) ? 52 : \ - (n) & (1ULL << 51) ? 51 : \ - (n) & (1ULL << 50) ? 50 : \ - (n) & (1ULL << 49) ? 49 : \ - (n) & (1ULL << 48) ? 48 : \ - (n) & (1ULL << 47) ? 47 : \ - (n) & (1ULL << 46) ? 46 : \ - (n) & (1ULL << 45) ? 45 : \ - (n) & (1ULL << 44) ? 44 : \ - (n) & (1ULL << 43) ? 43 : \ - (n) & (1ULL << 42) ? 42 : \ - (n) & (1ULL << 41) ? 41 : \ - (n) & (1ULL << 40) ? 40 : \ - (n) & (1ULL << 39) ? 39 : \ - (n) & (1ULL << 38) ? 38 : \ - (n) & (1ULL << 37) ? 37 : \ - (n) & (1ULL << 36) ? 36 : \ - (n) & (1ULL << 35) ? 35 : \ - (n) & (1ULL << 34) ? 34 : \ - (n) & (1ULL << 33) ? 33 : \ - (n) & (1ULL << 32) ? 32 : \ - (n) & (1ULL << 31) ? 31 : \ - (n) & (1ULL << 30) ? 30 : \ - (n) & (1ULL << 29) ? 29 : \ - (n) & (1ULL << 28) ? 28 : \ - (n) & (1ULL << 27) ? 27 : \ - (n) & (1ULL << 26) ? 26 : \ - (n) & (1ULL << 25) ? 25 : \ - (n) & (1ULL << 24) ? 24 : \ - (n) & (1ULL << 23) ? 23 : \ - (n) & (1ULL << 22) ? 22 : \ - (n) & (1ULL << 21) ? 21 : \ - (n) & (1ULL << 20) ? 20 : \ - (n) & (1ULL << 19) ? 19 : \ - (n) & (1ULL << 18) ? 18 : \ - (n) & (1ULL << 17) ? 17 : \ - (n) & (1ULL << 16) ? 16 : \ - (n) & (1ULL << 15) ? 15 : \ - (n) & (1ULL << 14) ? 14 : \ - (n) & (1ULL << 13) ? 13 : \ - (n) & (1ULL << 12) ? 12 : \ - (n) & (1ULL << 11) ? 11 : \ - (n) & (1ULL << 10) ? 10 : \ - (n) & (1ULL << 9) ? 9 : \ - (n) & (1ULL << 8) ? 8 : \ - (n) & (1ULL << 7) ? 7 : \ - (n) & (1ULL << 6) ? 6 : \ - (n) & (1ULL << 5) ? 5 : \ - (n) & (1ULL << 4) ? 4 : \ - (n) & (1ULL << 3) ? 3 : \ - (n) & (1ULL << 2) ? 2 : \ - 1 ) : \ - (sizeof(n) <= 4) ? \ - __ilog2_u32(n) : \ - __ilog2_u64(n) \ - ) - +#define ilog2(n) \ + (__builtin_constant_p(n) ? ((n) < 2 ? 0 \ + : (n) & (1ULL << 63) ? 63 \ + : (n) & (1ULL << 62) ? 62 \ + : (n) & (1ULL << 61) ? 61 \ + : (n) & (1ULL << 60) ? 60 \ + : (n) & (1ULL << 59) ? 59 \ + : (n) & (1ULL << 58) ? 58 \ + : (n) & (1ULL << 57) ? 57 \ + : (n) & (1ULL << 56) ? 56 \ + : (n) & (1ULL << 55) ? 55 \ + : (n) & (1ULL << 54) ? 54 \ + : (n) & (1ULL << 53) ? 53 \ + : (n) & (1ULL << 52) ? 52 \ + : (n) & (1ULL << 51) ? 51 \ + : (n) & (1ULL << 50) ? 50 \ + : (n) & (1ULL << 49) ? 49 \ + : (n) & (1ULL << 48) ? 48 \ + : (n) & (1ULL << 47) ? 47 \ + : (n) & (1ULL << 46) ? 46 \ + : (n) & (1ULL << 45) ? 45 \ + : (n) & (1ULL << 44) ? 44 \ + : (n) & (1ULL << 43) ? 43 \ + : (n) & (1ULL << 42) ? 42 \ + : (n) & (1ULL << 41) ? 41 \ + : (n) & (1ULL << 40) ? 40 \ + : (n) & (1ULL << 39) ? 39 \ + : (n) & (1ULL << 38) ? 38 \ + : (n) & (1ULL << 37) ? 37 \ + : (n) & (1ULL << 36) ? 36 \ + : (n) & (1ULL << 35) ? 35 \ + : (n) & (1ULL << 34) ? 34 \ + : (n) & (1ULL << 33) ? 33 \ + : (n) & (1ULL << 32) ? 32 \ + : (n) & (1ULL << 31) ? 31 \ + : (n) & (1ULL << 30) ? 30 \ + : (n) & (1ULL << 29) ? 29 \ + : (n) & (1ULL << 28) ? 28 \ + : (n) & (1ULL << 27) ? 27 \ + : (n) & (1ULL << 26) ? 26 \ + : (n) & (1ULL << 25) ? 25 \ + : (n) & (1ULL << 24) ? 24 \ + : (n) & (1ULL << 23) ? 23 \ + : (n) & (1ULL << 22) ? 22 \ + : (n) & (1ULL << 21) ? 21 \ + : (n) & (1ULL << 20) ? 20 \ + : (n) & (1ULL << 19) ? 19 \ + : (n) & (1ULL << 18) ? 18 \ + : (n) & (1ULL << 17) ? 17 \ + : (n) & (1ULL << 16) ? 16 \ + : (n) & (1ULL << 15) ? 15 \ + : (n) & (1ULL << 14) ? 14 \ + : (n) & (1ULL << 13) ? 13 \ + : (n) & (1ULL << 12) ? 12 \ + : (n) & (1ULL << 11) ? 11 \ + : (n) & (1ULL << 10) ? 10 \ + : (n) & (1ULL << 9) ? 9 \ + : (n) & (1ULL << 8) ? 8 \ + : (n) & (1ULL << 7) ? 7 \ + : (n) & (1ULL << 6) ? 6 \ + : (n) & (1ULL << 5) ? 5 \ + : (n) & (1ULL << 4) ? 4 \ + : (n) & (1ULL << 3) ? 3 \ + : (n) & (1ULL << 2) ? 2 \ + : 1) \ + : (sizeof(n) <= 4) ? __ilog2_u32(n) \ + : __ilog2_u64(n)) #if BITS_PER_LONG == 32 #define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32 @@ -221,14 +213,29 @@ static inline uint32_t hash32_ptr(const void *ptr) static inline uint32_t hash_string(const char *s) { - uint32_t h = 0; + uint32_t h = 0; - while (*s) { - h = h * 31 + *s; - s++; - } + while (*s) { + h = h * 31 + *s; + s++; + } - return h; + return h; +} + +static inline uint32_t hash_string_array(const char **a) +{ + uint32_t h = 0; + + const char *s; + while ((s = *a++)) { + while (*s) { + h = h * 31 + *s; + s++; + } + } + + return h; } #endif /* _GENERIC_HASH_H */ diff --git a/src/util.c b/src/util.c index f77885d..36dc019 100644 --- a/src/util.c +++ b/src/util.c @@ -18,10 +18,11 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE +#include #endif -#include "util.h" #include "dns_conf.h" #include "tlog.h" +#include "util.h" #include #include #include @@ -46,6 +47,10 @@ #include #include +#ifdef WITH_NFTSET +#include +#endif + #define TMP_BUFF_LEN_32 32 #define NFNL_SUBSYS_IPSET 6 @@ -636,6 +641,70 @@ int ipset_del(const char *ipsetname, const unsigned char addr[], int addr_len) return _ipset_operate(ipsetname, addr, addr_len, 0, IPSET_DEL); } +#ifdef WITH_NFTSET +static struct nft_ctx *_nftset_init(void) +{ + static struct nft_ctx *nft_ctx = NULL; + if (nft_ctx) { + return nft_ctx; + } + + nft_ctx = nft_ctx_new(NFT_CTX_DEFAULT); + if (!nft_ctx) { + return NULL; + } + + nft_ctx_buffer_error(nft_ctx); + return nft_ctx; +} + +static int _nftset_operate(const char *familyname, const char *tablename, const char *setname, + const unsigned char addr[], int af, const char *op, const char *flags) +{ + char cmd_buf[1024] = {'\0'}; + + struct nft_ctx *nft_ctx = _nftset_init(); + if (nft_ctx == NULL) { + return -1; + } + + char addr_str[INET6_ADDRSTRLEN]; + if (!inet_ntop(af, addr, addr_str, INET6_ADDRSTRLEN)) { + return -1; + } + + int ret = snprintf(cmd_buf, sizeof(cmd_buf), "%s element %s %s %s { %s %s }", op, familyname, tablename, setname, + addr_str, flags); + + if (ret == -1) { + return -1; + } + + ret = nft_run_cmd_from_buffer(nft_ctx, cmd_buf); + nft_ctx_get_error_buffer(nft_ctx); + + return ret; +} + +int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[], + int addr_len, unsigned long timeout) +{ + char flag_timeout[32] = {'\0'}; + int af = addr_len == IPV6_ADDR_LEN ? AF_INET6 : AF_INET; + if (dns_conf_nftset_timeout_enable) { + snprintf(flag_timeout, sizeof(flag_timeout), "timeout %lus", timeout); + } + return _nftset_operate(familyname, tablename, setname, addr, af, "add", flag_timeout); +} + +int nftset_del(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[], + int addr_len) +{ + int af = addr_len == IPV6_ADDR_LEN ? AF_INET6 : AF_INET; + return _nftset_operate(familyname, tablename, setname, addr, af, "delete", ""); +} +#endif + unsigned char *SSL_SHA256(const unsigned char *d, size_t n, unsigned char *md) { static unsigned char m[SHA256_DIGEST_LENGTH]; diff --git a/src/util.h b/src/util.h index 4f1c2be..65fa159 100644 --- a/src/util.h +++ b/src/util.h @@ -46,9 +46,7 @@ extern "C" { #define MAX_IP_LEN 64 #ifndef BASE_FILE_NAME -#define BASE_FILE_NAME \ - (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \ - : __FILE__) +#define BASE_FILE_NAME (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) #endif #define BUG(format, ...) bug_ext(BASE_FILE_NAME, __LINE__, __func__, format, ##__VA_ARGS__) @@ -83,6 +81,14 @@ int ipset_add(const char *ipsetname, const unsigned char addr[], int addr_len, u int ipset_del(const char *ipsetname, const unsigned char addr[], int addr_len); +#ifdef WITH_NFTSET +int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[], + int addr_len, unsigned long timeout); + +int nftset_del(const char *faimlyname, const char *tablename, const char *setname, const unsigned char addr[], + int addr_len); +#endif + void SSL_CRYPTO_thread_setup(void); void SSL_CRYPTO_thread_cleanup(void);