4#if defined(BSC_HAS_NFQ)
8#include <linux/netfilter.h>
9#include <linux/netfilter/nfnetlink_queue.h>
10#include <linux/types.h>
11#include <libnetfilter_queue/libnetfilter_queue.h>
13#include <netinet/ip.h>
14#include <netinet/ip6.h>
15#include <netinet/tcp.h>
16#include <netinet/udp.h>
17#include <sys/socket.h>
23constexpr int kAF_INET = 2;
24constexpr int kAF_INET6 = 10;
27 bsc::model::Connection conn;
31std::string to_ipv4(std::uint32_t ip) {
34 char buf[INET_ADDRSTRLEN]{};
35 inet_ntop(
AF_INET, &a, buf,
sizeof(buf));
39std::string to_ipv6(
const in6_addr& addr) {
40 char buf[INET6_ADDRSTRLEN]{};
41 inet_ntop(
AF_INET6, &addr, buf,
sizeof(buf));
47 if (!data || len < 1)
return out;
49 const auto version =
static_cast<std::uint8_t
>(
data[0] >> 4);
51 if (len <
static_cast<int>(
sizeof(iphdr)))
return out;
52 const auto* ip =
reinterpret_cast<const iphdr*
>(
data);
53 const auto ihl =
static_cast<int>(ip->ihl) * 4;
54 if (len < ihl)
return out;
55 out.conn.src_ip = to_ipv4(ip->saddr);
56 out.conn.dst_ip = to_ipv4(ip->daddr);
58 if (len < ihl +
static_cast<int>(
sizeof(tcphdr)))
return out;
59 const auto*
tcp =
reinterpret_cast<const tcphdr*
>(
data + ihl);
60 out.conn.protocol =
"tcp";
61 out.conn.src_port = ntohs(
tcp->source);
62 out.conn.dst_port = ntohs(
tcp->dest);
64 if (len < ihl +
static_cast<int>(
sizeof(udphdr)))
return out;
65 const auto* udp =
reinterpret_cast<const udphdr*
>(
data + ihl);
66 out.conn.protocol =
"udp";
67 out.conn.src_port = ntohs(udp->source);
68 out.conn.dst_port = ntohs(udp->dest);
77 if (len <
static_cast<int>(
sizeof(ip6_hdr)))
return out;
78 const auto* ip6 =
reinterpret_cast<const ip6_hdr*
>(
data);
79 out.conn.src_ip = to_ipv6(ip6->ip6_src);
80 out.conn.dst_ip = to_ipv6(ip6->ip6_dst);
81 const auto nexthdr = ip6->ip6_nxt;
82 const auto offset =
static_cast<int>(
sizeof(ip6_hdr));
84 if (len < offset +
static_cast<int>(
sizeof(tcphdr)))
return out;
85 const auto*
tcp =
reinterpret_cast<const tcphdr*
>(
data + offset);
86 out.conn.protocol =
"tcp6";
87 out.conn.src_port = ntohs(
tcp->source);
88 out.conn.dst_port = ntohs(
tcp->dest);
90 if (len < offset +
static_cast<int>(
sizeof(udphdr)))
return out;
91 const auto* udp =
reinterpret_cast<const udphdr*
>(
data + offset);
92 out.conn.protocol =
"udp6";
93 out.conn.src_port = ntohs(udp->source);
94 out.conn.dst_port = ntohs(udp->dest);
104static int nfq_callback(nfq_q_handle* qh, nfgenmsg*, nfq_data* nfa,
void* data);
108 std::uint16_t queue_id{};
109 NFQueue::Handler handler;
110 nfq_handle* handle{
nullptr};
111 nfq_q_handle* queue{
nullptr};
114 std::atomic_bool running{
false};
116 ~QueueOwner() { stop(); }
120 if (!handle)
return false;
121 if (nfq_unbind_pf(handle, kAF_INET) < 0 && errno != ENOENT) {
124 if (nfq_unbind_pf(handle, kAF_INET6) < 0 && errno != ENOENT) {
127 if (nfq_bind_pf(handle, kAF_INET) < 0 || nfq_bind_pf(handle, kAF_INET6) < 0) {
131 queue = nfq_create_queue(handle, queue_id, &nfq_callback,
this);
136 if (nfq_set_mode(queue, NFQNL_COPY_PACKET, 0xffff) < 0) {
140 nfq_set_queue_maxlen(queue, 4096);
141#ifdef NFQA_CFG_F_UID_GID
142 nfq_set_queue_flags(queue, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID);
146 thread = std::thread([
this]() {
147 char buf[8192] __attribute__((aligned));
149 const int rv = recv(fd, buf,
sizeof(buf), 0);
151 nfq_handle_packet(handle, buf, rv);
154 if (errno == EINTR)
continue;
167 ::shutdown(fd, SHUT_RDWR);
171 if (thread.joinable()) thread.join();
173 nfq_destroy_queue(queue);
183static int nfq_callback(nfq_q_handle* qh, nfgenmsg*, nfq_data* nfa,
void* data) {
184 auto* owner =
reinterpret_cast<QueueOwner*
>(
data);
185 if (!owner)
return 0;
187 std::uint32_t
id = 0;
188 if (
auto* ph = nfq_get_msg_packet_hdr(nfa))
id = ntohl(ph->packet_id);
190 unsigned char* payload =
nullptr;
191 const int payload_len = nfq_get_payload(nfa, &payload);
195 info.queue_id = owner->queue_id;
196 info.mark = nfq_get_nfmark(nfa);
197 info.in_dev = nfq_get_indev(nfa);
198 info.out_dev = nfq_get_outdev(nfa);
199#ifdef NFQA_CFG_F_UID_GID
200 std::uint32_t uid = 0xffffffff;
201 if (nfq_get_uid(nfa, &uid) >= 0) info.uid = uid;
203 if (parsed.ok) info.connection = std::move(parsed.conn);
206 if (info.uid != 0xffffffff && info.connection.user_id == 0xffffffff) info.connection.user_id = info.uid;
209 if (owner->handler) decision = owner->handler(info);
210 return nfq_set_verdict2(qh,
id,
static_cast<std::uint32_t
>(decision.verdict), decision.set_mark ? decision.mark : 0, 0,
nullptr);
216 std::unique_ptr<QueueOwner> owner;
220NFQueue::~NFQueue() { close(); }
222bool NFQueue::open(std::uint16_t queue_id, Handler handler) {
224 impl_->owner = std::make_unique<QueueOwner>();
225 impl_->owner->queue_id = queue_id;
226 impl_->owner->handler = std::move(handler);
227 running_ = impl_->owner->start();
231void NFQueue::close() {
233 if (impl_ && impl_->owner) {
234 impl_->owner->stop();
235 impl_->owner.reset();
void warning(const std::string &msg)
static Logger & instance()
bool open(std::uint16_t queue_id, Handler handler)
std::unique_ptr< Impl > impl_
std::function< Decision(const PacketInfo &)> Handler
std::map< std::string, std::string > parse_payload(const std::string &payload)