From 85d011eae82f14918f8cda5ae9c950f98da49300 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Tue, 15 Nov 2022 22:31:34 +0800 Subject: [PATCH] nftset: Remove libnftable dependency --- ReadMe.md | 7 +- ReadMe_en.md | 7 +- etc/smartdns/smartdns.conf | 13 ++ package/build-pkg.sh | 4 - src/Makefile | 7 +- src/dns_conf.c | 7 +- src/dns_conf.h | 1 + src/dns_server.c | 22 ++- src/include/nftset.h | 36 ++++ src/lib/nftset.c | 378 +++++++++++++++++++++++++++++++++++++ src/util.c | 71 +------ src/util.h | 8 - 12 files changed, 456 insertions(+), 105 deletions(-) create mode 100644 src/include/nftset.h create mode 100644 src/lib/nftset.c diff --git a/ReadMe.md b/ReadMe.md index 271b0d6..52ed56b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -545,10 +545,11 @@ entware|ipkg update
ipkg install smartdns|软件源路径:https://bin.entw | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6]
- 表示忽略
# 表示返回 SOA
4 表示 IPv4
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...]
[-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 | +| nftset-timeout | 设置 nftset 超时功能启用 | no | [yes\|no] | nftset-timeout yes | +| nftset-debug | 设置 nftset 调试功能启用 | no | [yes\|no] | nftset-debug yes | +| domain-rules | 设置域名规则 | 无 | domain-rules /domain/ [-rules...]
[-c\|-speed-check-mode]:测速模式,参考 speed-check-mode 配置
[-a\|-address]:参考 address 配置
[-n\|-nameserver]:参考 nameserver 配置
[-p\|-ipset]:参考ipset配置
[-t\|-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 | diff --git a/ReadMe_en.md b/ReadMe_en.md index b7cca64..830e01d 100644 --- a/ReadMe_en.md +++ b/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...]
`[-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 +|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...]
`[-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`
`[-t\|-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 diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 6ae7405..f985c9d 100644 --- a/etc/smartdns/smartdns.conf +++ b/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 diff --git a/package/build-pkg.sh b/package/build-pkg.sh index a21312d..eaa06bd 100755 --- a/package/build-pkg.sh +++ b/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;; diff --git a/src/Makefile b/src/Makefile index e3ece41..a166a8a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,7 +15,7 @@ # along with this program. If not, see . 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) diff --git a/src/dns_conf.c b/src/dns_conf.c index 1bcc37b..79da4a3 100644 --- a/src/dns_conf.c +++ b/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), diff --git a/src/dns_conf.h b/src/dns_conf.h index 980cc76..166da40 100644 --- a/src/dns_conf.h +++ b/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; diff --git a/src/dns_server.c b/src/dns_server.c index 1c1b565..3938580 100644 --- a/src/dns_server.c +++ b/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 @@ -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; diff --git a/src/include/nftset.h b/src/include/nftset.h new file mode 100644 index 0000000..699fac4 --- /dev/null +++ b/src/include/nftset.h @@ -0,0 +1,36 @@ +/************************************************************************* + * + * Copyright (C) 2018-2022Ruilin Peng (Nick) . + * + * 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 . + */ + +#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 diff --git a/src/lib/nftset.c b/src/lib/nftset.c new file mode 100644 index 0000000..caebdf7 --- /dev/null +++ b/src/lib/nftset.c @@ -0,0 +1,378 @@ +/************************************************************************* + * + * Copyright (C) 2018-2022 Ruilin Peng (Nick) . + * + * 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 . + */ + +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include "nftset.h" +#include "../dns_conf.h" +#include "../tlog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/src/util.c b/src/util.c index 36dc019..938d1a9 100644 --- a/src/util.c +++ b/src/util.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -47,10 +48,6 @@ #include #include -#ifdef WITH_NFTSET -#include -#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 \ No newline at end of file +#endif diff --git a/src/util.h b/src/util.h index 65fa159..ba17dea 100644 --- a/src/util.h +++ b/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);