Jelajahi Sumber

nftset: Remove libnftable dependency

Nick Peng 2 tahun lalu
induk
melakukan
85d011eae8
12 mengubah file dengan 456 tambahan dan 105 penghapusan
  1. 4 3
      ReadMe.md
  2. 4 3
      ReadMe_en.md
  3. 13 0
      etc/smartdns/smartdns.conf
  4. 0 4
      package/build-pkg.sh
  5. 1 6
      src/Makefile
  6. 5 2
      src/dns_conf.c
  7. 1 0
      src/dns_conf.h
  8. 12 10
      src/dns_server.c
  9. 36 0
      src/include/nftset.h
  10. 378 0
      src/lib/nftset.c
  11. 2 69
      src/util.c
  12. 0 8
      src/util.h

+ 4 - 3
ReadMe.md

@@ -545,10 +545,11 @@ entware|ipkg update</br>ipkg install smartdns|软件源路径:https://bin.entw
 | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br>- 表示忽略 <br># 表示返回 SOA <br>4 表示 IPv4 <br>6 表示 IPv6 | address /www.example.com/1.2.3.4 |
 | 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 |
+| ipset-timeout | 设置 ipset 超时功能启用  | no | [yes\|no] | ipset-timeout yes |
 | 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 |
+| nftset-timeout | 设置 nftset 超时功能启用  | no | [yes\|no] | nftset-timeout yes |
+| nftset-debug | 设置 nftset 调试功能启用  | no | [yes\|no] | nftset-debug 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>[-t\|-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 |

+ 4 - 3
ReadMe_en.md

@@ -504,10 +504,11 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
 |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
+|ipset-timeout|ipset timeout enable|no|[yes\|no]|ipset-timeout yes
 |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
+|nftset-timeout|nftset timeout enable|no|[yes\|no]|nftset-timeout yes
+|nftset-debug|nftset debug enable|no|[yes\|no]|nftset-debug 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>`[-t\|-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

+ 13 - 0
etc/smartdns/smartdns.conf

@@ -213,6 +213,18 @@ log-level info
 # ipset /www.example.com/block, set ipset with ipset name of block 
 # ipset /www.example.com/-, ignore this domain
 
+# enable nftset timeout by ttl feature
+# nftset-timeout [yes]
+
+# enable nftset debug, check nftset setting result, output log when error.
+# nftset-debug [no]
+
+# specific nftset to domain
+# nftset /domain/[#4:ip#table#set,#6:ipv6#table#setv6]
+# nftset /www.example.com/ip#table#set, equivalent to 'nft add element ip table set { ... }'
+# nftset /www.example.com/-, ignore this domain
+# nftset /www.example.com/#6:-, ignore ipv6
+
 # set domain rules
 # domain-rules /domain/ [-speed-check-mode [...]]
 # rules:
@@ -221,6 +233,7 @@ log-level info
 #   [-a] -address [address|-]: same as address option
 #   [-n] -nameserver [group|-]: same as nameserver option
 #   [-p] -ipset [ipset|-]: same as ipset option
+#   [-t] -nftset [nftset|-]: same as nftset option
 #   [-d] -dualstack-ip-selection [yes|no]: same as dualstack-ip-selection option
 
 # collection of domains 

+ 0 - 4
package/build-pkg.sh

@@ -16,7 +16,6 @@ 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"
@@ -109,9 +108,6 @@ main()
 		--cross-tool)
 			CROSS_TOOL="$2"
 			shift 2;;
-		 --with-nftables)
-			MAKE_ARGS="WITH_NFTSET=1"
-			shift 1;;
 		--static)
 			export STATIC="yes"
 			shift 1;;

+ 1 - 6
src/Makefile

@@ -15,7 +15,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 BIN=smartdns 
-OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o
+OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.o
 OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o $(OBJS_LIB)
 
 # cflags
@@ -38,11 +38,6 @@ else
 override LDFLAGS += -lssl -lcrypto -lpthread -ldl
 endif
 
-ifdef WITH_NFTSET
-override CFLAGS += -DWITH_NFTSET
-override LDFLAGS += -lnftables
-endif
-
 .PHONY: all clean
 
 all: $(BIN)

+ 5 - 2
src/dns_conf.c

@@ -139,6 +139,7 @@ int dns_conf_force_AAAA_SOA;
 int dns_conf_force_no_cname;
 int dns_conf_ipset_timeout_enable;
 int dns_conf_nftset_timeout_enable;
+int dns_conf_nftset_debug_enable;
 
 char dns_conf_user[DNS_CONF_USRNAME_LEN];
 
@@ -984,6 +985,7 @@ static int _conf_domain_rule_nftset(char *domain, const char *nftsetname)
 			goto errout;
 		}
 		_dns_rule_put(&nftset_rule->head);
+		nftset_rule = NULL;
 	}
 
 	goto clear;
@@ -1834,7 +1836,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'},
+		{"nftset", required_argument, NULL, 't'},
 		{"nameserver", required_argument, NULL, 'n'},
 		{"dualstack-ip-selection", required_argument, NULL, 'd'},
 		{NULL, no_argument, NULL, 0}
@@ -1920,7 +1922,7 @@ static int _conf_domain_rules(void *data, int argc, char *argv[])
 
 			break;
 		}
-		case 's': {
+		case 't': {
 			const char *nftsetname = optarg;
 			if (nftsetname == NULL) {
 				goto errout;
@@ -2362,6 +2364,7 @@ static struct config_item _config_item[] = {
 	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_YESNO("nftset-debug", &dns_conf_nftset_debug_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),

+ 1 - 0
src/dns_conf.h

@@ -389,6 +389,7 @@ 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_nftset_debug_enable;
 extern int dns_conf_local_ttl;
 
 extern int dns_conf_force_no_cname;

+ 12 - 10
src/dns_server.c

@@ -28,6 +28,7 @@
 #include "fast_ping.h"
 #include "hashtable.h"
 #include "list.h"
+#include "nftset.h"
 #include "tlog.h"
 #include "util.h"
 #include <errno.h>
@@ -1397,15 +1398,19 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
 	if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IGN) == 0) {
 		ipset_rule = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET);
 	}
+
 	if (!rule_flags || (rule_flags->flags & DOMAIN_FLAG_IPSET_IPV4_IGN) == 0) {
 		ipset_rule_v4 = _dns_server_get_dns_rule(request, DOMAIN_RULE_IPSET_IPV4);
 	}
+
 	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);
 	}
@@ -1429,21 +1434,19 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
 				rule = ipset_rule_v4 ? ipset_rule_v4 : ipset_rule;
 				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]);
+					ipset_add(rule->ipsetname, addr, DNS_RR_A_LEN, request->ip_ttl * 2);
 				}
 
-#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]);
+					nftset_add(nftset_ip->familyname, nftset_ip->nfttablename, nftset_ip->nftsetname, addr,
+							   DNS_RR_A_LEN, request->ip_ttl * 2);					
 				}
-#endif
 			} break;
 			case DNS_T_AAAA: {
 				unsigned char addr[16];
@@ -1455,27 +1458,26 @@ static int _dns_server_setup_ipset_nftset_packet(struct dns_server_post_context
 
 				rule = ipset_rule_v6 ? ipset_rule_v6 : ipset_rule;
 				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);
 				}
-#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]);
+					nftset_add(nftset_ip6->familyname, nftset_ip6->nfttablename, nftset_ip6->nftsetname, addr,
+							   DNS_RR_AAAA_LEN, request->ip_ttl * 2);
 				}
-#endif
 			} break;
 			default:
 				break;

+ 36 - 0
src/include/nftset.h

@@ -0,0 +1,36 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2022Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NFTSET_H
+#define _NFTSET_H
+
+#ifdef __cpluscplus
+extern "C" {
+#endif
+
+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 *familyname, const char *tablename, const char *setname, const unsigned char addr[],
+			   int addr_len);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif // !_NFTSET_H

+ 378 - 0
src/lib/nftset.c

@@ -0,0 +1,378 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2022 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * smartdns is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * smartdns is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include "nftset.h"
+#include "../dns_conf.h"
+#include "../tlog.h"
+#include <errno.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <memory.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+struct nlmsgreq {
+	struct nlmsghdr h;
+	struct nfgenmsg m;
+};
+
+enum { PAYLOAD_MAX = 2048 };
+
+static int nftset_fd;
+
+static struct rtattr *_nftset_nlmsg_tail(struct nlmsghdr *n)
+{
+	return (struct rtattr *)((uint8_t *)n + NLMSG_ALIGN(n->nlmsg_len));
+}
+
+static int _nftset_addattr(struct nlmsghdr *n, int maxlen, __u16 type, const void *data, __u16 alen)
+{
+	const __u16 len = RTA_LENGTH(alen);
+	const ssize_t newlen = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+	if (newlen > maxlen) {
+		errno = ENOSPC;
+		return -1;
+	}
+
+	struct rtattr *attr = _nftset_nlmsg_tail(n);
+	attr->rta_len = len;
+	attr->rta_type = type;
+
+	void *rta_data = RTA_DATA(attr);
+
+	memcpy(rta_data, data, alen);
+
+	n->nlmsg_len = newlen;
+
+	return 0;
+}
+
+static int _nftset_addattr_string(struct nlmsghdr *n, int maxlen, __u16 type, const char *s)
+{
+	return _nftset_addattr(n, maxlen, type, s, strlen(s) + 1);
+}
+
+static int __attribute__((unused)) _nftset_addattr_uint32(struct nlmsghdr *n, int maxlen, __u16 type, const uint32_t v)
+{
+	return _nftset_addattr(n, maxlen, type, &v, sizeof(uint32_t));
+}
+
+static int __attribute__((unused)) _nftset_addattr_uint16(struct nlmsghdr *n, int maxlen, __u16 type, const uint16_t v)
+{
+	return _nftset_addattr(n, maxlen, type, &v, sizeof(uint16_t));
+}
+
+static int __attribute__((unused)) _nftset_addattr_uint8(struct nlmsghdr *n, int maxlen, __u16 type, const uint8_t v)
+{
+	return _nftset_addattr(n, maxlen, type, &v, sizeof(uint8_t));
+}
+
+static struct rtattr *_nftset_addattr_nest(struct nlmsghdr *n, int maxlen, __u16 type)
+{
+	struct rtattr *attr = _nftset_nlmsg_tail(n);
+
+	if (-1 == _nftset_addattr(n, maxlen, type, NULL, 0)) {
+		return NULL;
+	}
+
+	return attr;
+}
+
+static void _nftset_addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+	const void *tail = _nftset_nlmsg_tail(n);
+	nest->rta_len = (uint8_t *)tail - (uint8_t *)nest;
+}
+
+static int _nftset_start_batch(void *buf, void **nextbuf)
+{
+	struct nlmsgreq *req = (struct nlmsgreq *)buf;
+	memset(buf, 0, sizeof(struct nlmsgreq));
+
+	req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	req->h.nlmsg_flags = NLM_F_REQUEST;
+	req->h.nlmsg_type = NFNL_MSG_BATCH_BEGIN;
+	req->h.nlmsg_seq = time(NULL);
+
+	req->m.res_id = NFNL_SUBSYS_NFTABLES;
+
+	if (nextbuf) {
+		*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
+	}
+	return 0;
+}
+
+static int _nftset_end_batch(void *buf, void **nextbuf)
+{
+	struct nlmsgreq *req = (struct nlmsgreq *)buf;
+	memset(buf, 0, sizeof(struct nlmsgreq));
+
+	req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	req->h.nlmsg_flags = NLM_F_REQUEST;
+	req->h.nlmsg_type = NFNL_MSG_BATCH_END;
+	req->h.nlmsg_seq = time(NULL);
+
+	req->m.res_id = NFNL_SUBSYS_NFTABLES;
+
+	if (nextbuf) {
+		*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
+	}
+
+	return 0;
+}
+
+static int _nftset_socket_init(void)
+{
+	struct sockaddr_nl addr = {0};
+	addr.nl_family = AF_NETLINK;
+	addr.nl_pid = 0;
+	int fd = 0;
+
+	if (nftset_fd > 0) {
+		return 0;
+	}
+
+	fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_NETFILTER);
+	if (fd < 0) {
+		return -1;
+	}
+
+	if (bind(fd, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
+		close(fd);
+		return -2;
+	}
+
+	nftset_fd = fd;
+
+	return 0;
+}
+
+static int _nftset_socket_send(void *msg, int msg_len)
+{
+	char recvbuff[1024];
+	int ret = -1;
+	struct pollfd pfds;
+	int do_recv = 0;
+	int last_errno = 0;
+
+	if (_nftset_socket_init() != 0) {
+		return -1;
+	}
+
+	for (;;) {
+		int len = send(nftset_fd, msg, msg_len, 0);
+		if (len == msg_len) {
+			break;
+		}
+
+		if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+			struct timespec waiter;
+			waiter.tv_sec = 0;
+			waiter.tv_nsec = 10000;
+			nanosleep(&waiter, NULL);
+			continue;
+		}
+
+		return -1;
+	}
+
+	if (dns_conf_nftset_debug_enable == 0) {
+		return 0;
+	}
+
+	pfds.fd = nftset_fd;
+	pfds.events = POLLIN;
+	pfds.revents = 0;
+	ret = poll(&pfds, 1, 100);
+	if (ret <= 0) {
+		return -1;
+	}
+
+	if ((pfds.revents & POLLIN) == 0) {
+		return -1;
+	}
+
+	memset(recvbuff, 0, sizeof(recvbuff));
+	for (;;) {
+		ret = recv(nftset_fd, recvbuff, 1024, 0);
+		if (ret < 0) {
+			if (errno == EAGAIN && do_recv == 1) {
+				break;
+			}
+
+			if (errno == EAGAIN && last_errno != 0) {
+				errno = last_errno;
+			}
+			return -1;
+		}
+
+		do_recv = 1;
+
+		struct nlmsghdr *nlh = (struct nlmsghdr *)recvbuff;
+		if (nlh->nlmsg_type == NLMSG_ERROR) {
+			struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh);
+			if (err->error != 0) {
+				errno = -err->error;
+				last_errno = errno;
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int _nftset_del_element(const char *table_name, const char *setname, const void *data, int data_len, void *buf,
+							   void **nextbuf)
+{
+	struct nlmsgreq *req = (struct nlmsgreq *)buf;
+	memset(buf, 0, sizeof(struct nlmsgreq));
+
+	req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	req->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+	req->h.nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | NFT_MSG_DELSETELEM;
+	req->h.nlmsg_seq = time(NULL);
+
+	if (dns_conf_nftset_debug_enable) {
+		req->h.nlmsg_flags |= NLM_F_ACK;
+	}
+
+	req->m.nfgen_family = NFPROTO_INET;
+
+	struct nlmsghdr *n = &req->h;
+
+	_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_TABLE, table_name);
+	_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_SET, setname);
+	struct rtattr *nest_list = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_LIST_ELEMENTS);
+	struct rtattr *nest_elem = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED);
+
+	struct rtattr *nest_elem_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
+	_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data, data_len);
+	_nftset_addattr_nest_end(n, nest_elem_key);
+	_nftset_addattr_nest_end(n, nest_elem);
+	_nftset_addattr_nest_end(n, nest_list);
+
+	if (nextbuf) {
+		*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
+	}
+
+	return 0;
+}
+
+static int _nftset_add_element(const char *table_name, const char *setname, const void *data, int data_len,
+							   unsigned long timeout, void *buf, void **nextbuf)
+{
+	struct nlmsgreq *req = (struct nlmsgreq *)buf;
+	memset(buf, 0, sizeof(struct nlmsgreq));
+
+	req->h.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg));
+	req->h.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
+	req->h.nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | NFT_MSG_NEWSETELEM;
+	req->h.nlmsg_seq = time(NULL);
+
+	if (dns_conf_nftset_debug_enable) {
+		req->h.nlmsg_flags |= NLM_F_ACK;
+	}
+
+	req->m.nfgen_family = NFPROTO_INET;
+
+	struct nlmsghdr *n = &req->h;
+
+	_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_TABLE, table_name);
+	_nftset_addattr_string(n, PAYLOAD_MAX, NFTA_SET_ELEM_LIST_SET, setname);
+	struct rtattr *nest_list = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_LIST_ELEMENTS);
+	struct rtattr *nest_elem = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED);
+
+	struct rtattr *nest_elem_key = _nftset_addattr_nest(n, PAYLOAD_MAX, NLA_F_NESTED | NFTA_SET_ELEM_KEY);
+	_nftset_addattr(n, PAYLOAD_MAX, NFTA_DATA_VALUE, data, data_len);
+	_nftset_addattr_nest_end(n, nest_elem_key);
+
+	if (timeout > 0) {
+		uint64_t timeout_value = htobe64(timeout * 1000);
+		_nftset_addattr(n, PAYLOAD_MAX, NFTA_SET_ELEM_TIMEOUT, &timeout_value, sizeof(timeout_value));
+	}
+
+	_nftset_addattr_nest_end(n, nest_elem);
+	_nftset_addattr_nest_end(n, nest_list);
+
+	if (nextbuf) {
+		*nextbuf = (uint8_t *)buf + req->h.nlmsg_len;
+	}
+
+	return 0;
+}
+
+int nftset_del(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
+			   int addr_len)
+{
+	uint8_t buf[PAYLOAD_MAX];
+	void *next = buf;
+	int buffer_len = 0;
+
+	_nftset_start_batch(next, &next);
+	_nftset_del_element(tablename, setname, addr, addr_len, next, &next);
+	_nftset_end_batch(next, &next);
+	buffer_len = (uint8_t *)next - buf;
+
+	int ret = _nftset_socket_send(buf, buffer_len);
+	if (ret != 0 && errno != ENOENT) {
+		tlog(TLOG_ERROR, "nftset delete failed, family:%s, table:%s, set:%s, error:%s", familyname, tablename, setname,
+			 strerror(errno));
+	}
+
+	return ret;
+}
+
+int nftset_add(const char *familyname, const char *tablename, const char *setname, const unsigned char addr[],
+			   int addr_len, unsigned long timeout)
+{
+	uint8_t buf[PAYLOAD_MAX];
+	void *next = buf;
+	int buffer_len = 0;
+	int ret = -1;
+
+	if (dns_conf_nftset_timeout_enable == 0) {
+		timeout = 0;
+	}
+
+	nftset_del(familyname, tablename, setname, addr, addr_len);
+	_nftset_start_batch(next, &next);
+	_nftset_add_element(tablename, setname, addr, addr_len, timeout, next, &next);
+	_nftset_end_batch(next, &next);
+	buffer_len = (uint8_t *)next - buf;
+
+	ret = _nftset_socket_send(buf, buffer_len);
+	if (ret != 0) {
+		tlog(TLOG_ERROR, "nftset add failed, family:%s, table:%s, set:%s, error:%s", familyname, tablename, setname,
+			 strerror(errno));
+	}
+
+	return ret;
+}

+ 2 - 69
src/util.c

@@ -31,6 +31,7 @@
 #include <linux/capability.h>
 #include <linux/limits.h>
 #include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 #include <netinet/tcp.h>
 #include <openssl/crypto.h>
 #include <openssl/ssl.h>
@@ -47,10 +48,6 @@
 #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
@@ -641,70 +638,6 @@ 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];
@@ -1523,4 +1456,4 @@ errout:
 	return -1;
 }
 
-#endif
+#endif

+ 0 - 8
src/util.h

@@ -81,14 +81,6 @@ 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);