소스 검색

Simple support for nftables (#1117)

* Simple support for nftables

Co-authored-by: Chen Zhenge <[email protected]>
LoveSy 3 년 전
부모
커밋
391ef310b4
11개의 변경된 파일451개의 추가작업 그리고 118개의 파일을 삭제
  1. 6 4
      .clang-format
  2. 6 4
      ReadMe_en.md
  3. 1 1
      etc/smartdns/smartdns.conf
  4. 5 1
      package/build-pkg.sh
  5. 6 1
      src/Makefile
  6. 187 0
      src/dns_conf.c
  7. 22 0
      src/dns_conf.h
  8. 48 19
      src/dns_server.c
  9. 91 84
      src/include/hash.h
  10. 70 1
      src/util.c
  11. 9 3
      src/util.h

+ 6 - 4
.clang-format

@@ -515,8 +515,8 @@ entware|ipkg update</br>ipkg install smartdns|软件源路径:https://bin.entw
 | 键名 | 功能说明 | 默认值 | 可用值/要求 | 举例 |
 | :--- | :--- | :--- | :--- | :--- |
 | server-name | DNS 服务器名称 | 操作系统主机名 / smartdns | 符合主机名规格的字符串 | server-name smartdns |
-| bind | DNS 监听端口号  | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 Nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 规则<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind :53 |
-| bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 规则。<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind-tcp :53 |
+| bind | DNS 监听端口号  | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 Nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 和 nftset 规则<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind :53 |
+| bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。<br>IP:PORT: 服务器 IP:端口号<br>[-group]: 请求时使用的 DNS 服务器组<br>[-no-rule-addr]:跳过 address 规则<br>[-no-rule-nameserver]:跳过 nameserver 规则<br>[-no-rule-ipset]:跳过 ipset 和 nftset 规则。<br>[-no-rule-soa]:跳过 SOA(#) 规则<br>[-no-dualstack-selection]:停用双栈测速<br>[-no-speed-check]:停用测速<br>[-no-cache]:停止缓存 | bind-tcp :53 |
 | cache-size | 域名结果缓存个数 | 512 | 大于等于 0 的数字 | cache-size 512 |
 | cache-persist | 是否持久化缓存 | 自动。<br>当 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</br>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...]<br>[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置<br>[-a\|-address]:参考 address 配置<br>[-n\|-nameserver]:参考 nameserver 配置<br>[-p\|-ipset]:参考ipset配置<br>[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection  | domain-rules /www.example.com/ -speed-check-mode none |
-| domain-set | 设置域名集合 | 无 | domain-set [options...]<br>[-n\|-name]:域名集合名称 <br>[-t\|-type]:域名集合类型,当前仅支持list,格式为域名列表,一行一个域名。<br>[-f\|-file]:域名集合文件路径。<br> 选项需要配合address, nameserver, ipset等需要指定域名的地方使用,使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list <br> 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...]<br>[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置<br>[-a\|-address]:参考 address 配置<br>[-n\|-nameserver]:参考 nameserver 配置<br>[-p\|-ipset]:参考ipset配置<br>[-s\|-nftset]:参考nftset配置<br>[-d\|-dualstack-ip-selection]:参考 dualstack-ip-selection  | domain-rules /www.example.com/ -speed-check-mode none |
+| domain-set | 设置域名集合 | 无 | domain-set [options...]<br>[-n\|-name]:域名集合名称 <br>[-t\|-type]:域名集合类型,当前仅支持list,格式为域名列表,一行一个域名。<br>[-f\|-file]:域名集合文件路径。<br> 选项需要配合address, nameserver, ipset, nftset等需要指定域名的地方使用,使用方式为 /domain-set:[name]/| domain-set -name set -type list -file /path/to/list <br> 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 |

+ 6 - 4
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<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind :53
-|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset rule. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind-tcp :53
+|bind|DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset or nftset rules. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-no-cache]`: stop caching |bind :53
+|bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br>`IP:PORT`: server IP, port number. <br>`[-group]`: The DNS server group used when requesting. <br>`[-no-rule-addr]`: Skip the address rule. <br>`[-no-rule-nameserver]`: Skip the Nameserver rule. <br>`[-no-rule-ipset]`: Skip the Ipset or nftset rules. <br>`[-no-rule-soa]`: Skip address SOA(#) rules.<br>`[-no-dualstack-selection]`: Disable dualstack ip selection.<br>`[-no-speed-check]`: Disable speed measurement. <br>`[-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...]<br>`[-c\|-speed-check-mode]`: set speed check mode,same as parameter `speed-check-mode`<br>`[-a\|-address]`: same as  parameter `address` <br>`[-n\|-nameserver]`: same as parameter `nameserver`<br>`[-p\|-ipset]`: same as parameter `ipset`<br>`[-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...]<br>[-n\|-name]:name of set <br>[-t\|-type] [list]: set type, only support list, one domain per line <br>[-f\|-file]:file path of domain set<br> used with address, nameserver, ipset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list <br> 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...]<br>`[-c\|-speed-check-mode]`: set speed check mode,same as parameter `speed-check-mode`<br>`[-a\|-address]`: same as  parameter `address` <br>`[-n\|-nameserver]`: same as parameter `nameserver`<br>`[-p\|-ipset]`: same as parameter `nftset`<br>`[-s\|-nftset]`: same as parameter `nftset`<br>`[-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...]<br>[-n\|-name]:name of set <br>[-t\|-type] [list]: set type, only support list, one domain per line <br>[-f\|-file]:file path of domain set<br> used with address, nameserver, ipset, nftset, example: /domain-set:[name]/ | domain-set -name set -type list -file /path/to/list <br> 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

+ 1 - 1
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.

+ 5 - 1
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;;

+ 6 - 1
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)
 

+ 187 - 0
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();

+ 22 - 0
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;

+ 48 - 19
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.");
 	}
 

+ 91 - 84
src/include/hash.h

@@ -16,7 +16,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #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++;
+	}
+
+	return h;
+}
+
+static inline uint32_t hash_string_array(const char **a)
+{
+	uint32_t h = 0;
 
-    while (*s) {
-        h = h * 31 + *s;
-        s++;
-    }
+	const char *s;
+	while ((s = *a++)) {
+		while (*s) {
+			h = h * 31 + *s;
+			s++;
+		}
+	}
 
-    return h;
+	return h;
 }
 
 #endif /* _GENERIC_HASH_H */

+ 70 - 1
src/util.c

@@ -18,10 +18,11 @@
 
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
+#include <stdio.h>
 #endif
-#include "util.h"
 #include "dns_conf.h"
 #include "tlog.h"
+#include "util.h"
 #include <arpa/inet.h>
 #include <dlfcn.h>
 #include <errno.h>
@@ -46,6 +47,10 @@
 #include <unistd.h>
 #include <unwind.h>
 
+#ifdef WITH_NFTSET
+#include <nftables/libnftables.h>
+#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];

+ 9 - 3
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);