2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "bpf-filter.hh"
28 #include <sys/syscall.h>
29 #include <sys/resource.h>
30 #include <linux/bpf.h>
32 #include "ext/libbpf/libbpf.h"
36 static __u64
ptr_to_u64(const void *ptr
)
38 return (__u64
) (unsigned long) ptr
;
41 /* these can be static as they are not declared in libbpf.h: */
42 static int bpf_pin_map(int descriptor
, const std::string
& path
)
45 memset(&attr
, 0, sizeof(attr
));
46 attr
.bpf_fd
= descriptor
;
47 attr
.pathname
= ptr_to_u64(path
.c_str());
48 return syscall(SYS_bpf
, BPF_OBJ_PIN
, &attr
, sizeof(attr
));
51 static int bpf_load_pinned_map(const std::string
& path
)
54 memset(&attr
, 0, sizeof(attr
));
55 attr
.pathname
= ptr_to_u64(path
.c_str());
56 return syscall(SYS_bpf
, BPF_OBJ_GET
, &attr
, sizeof(attr
));
59 static void bpf_check_map_sizes(int descriptor
, uint32_t expectedKeySize
, uint32_t expectedValueSize
)
61 struct bpf_map_info info
;
62 uint32_t info_len
= sizeof(info
);
63 memset(&info
, 0, sizeof(info
));
66 memset(&attr
, 0, sizeof(attr
));
67 attr
.info
.bpf_fd
= descriptor
;
68 attr
.info
.info_len
= info_len
;
69 attr
.info
.info
= ptr_to_u64(&info
);
71 int err
= syscall(SYS_bpf
, BPF_OBJ_GET_INFO_BY_FD
, &attr
, sizeof(attr
));
73 throw std::runtime_error("Error checking the size of eBPF map: " + stringerror());
75 if (info_len
!= sizeof(info
)) {
76 throw std::runtime_error("Error checking the size of eBPF map: invalid info size returned");
78 if (info
.key_size
!= expectedKeySize
) {
79 throw std::runtime_error("Error checking the size of eBPF map: key size mismatch (" + std::to_string(info
.key_size
) + " VS " + std::to_string(expectedKeySize
) + ")");
81 if (info
.value_size
!= expectedValueSize
) {
82 throw std::runtime_error("Error checking the size of eBPF map: value size mismatch (" + std::to_string(info
.value_size
) + " VS " + std::to_string(expectedValueSize
) + ")");
86 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
87 static int bpf_create_map(enum bpf_map_type map_type
, int key_size
, int value_size
,
88 int max_entries
, int map_flags
)
91 memset(&attr
, 0, sizeof(attr
));
92 attr
.map_type
= map_type
;
93 attr
.key_size
= key_size
;
94 attr
.value_size
= value_size
;
95 attr
.max_entries
= max_entries
;
96 attr
.map_flags
= map_flags
;
97 return syscall(SYS_bpf
, BPF_MAP_CREATE
, &attr
, sizeof(attr
));
100 static int bpf_update_elem(int descriptor
, void *key
, void *value
, unsigned long long flags
)
103 memset(&attr
, 0, sizeof(attr
));
104 attr
.map_fd
= descriptor
;
105 attr
.key
= ptr_to_u64(key
);
106 attr
.value
= ptr_to_u64(value
);
108 return syscall(SYS_bpf
, BPF_MAP_UPDATE_ELEM
, &attr
, sizeof(attr
));
111 static int bpf_lookup_elem(int descriptor
, void *key
, void *value
)
114 memset(&attr
, 0, sizeof(attr
));
115 attr
.map_fd
= descriptor
;
116 attr
.key
= ptr_to_u64(key
);
117 attr
.value
= ptr_to_u64(value
);
118 return syscall(SYS_bpf
, BPF_MAP_LOOKUP_ELEM
, &attr
, sizeof(attr
));
121 static int bpf_delete_elem(int descriptor
, void *key
)
124 memset(&attr
, 0, sizeof(attr
));
125 attr
.map_fd
= descriptor
;
126 attr
.key
= ptr_to_u64(key
);
127 return syscall(SYS_bpf
, BPF_MAP_DELETE_ELEM
, &attr
, sizeof(attr
));
130 static int bpf_get_next_key(int descriptor
, void *key
, void *next_key
)
133 memset(&attr
, 0, sizeof(attr
));
134 attr
.map_fd
= descriptor
;
135 attr
.key
= ptr_to_u64(key
);
136 attr
.next_key
= ptr_to_u64(next_key
);
137 return syscall(SYS_bpf
, BPF_MAP_GET_NEXT_KEY
, &attr
, sizeof(attr
));
140 static int bpf_prog_load(enum bpf_prog_type prog_type
,
141 const struct bpf_insn
*insns
, size_t prog_len
,
142 const char *license
, int kern_version
)
146 memset(&attr
, 0, sizeof(attr
));
147 attr
.prog_type
= prog_type
;
148 attr
.insns
= ptr_to_u64((void *) insns
);
149 attr
.insn_cnt
= static_cast<int>(prog_len
/ sizeof(struct bpf_insn
));
150 attr
.license
= ptr_to_u64((void *) license
);
151 attr
.log_buf
= ptr_to_u64(log_buf
);
152 attr
.log_size
= sizeof(log_buf
);
154 /* assign one field outside of struct init to make sure any
155 * padding is zero initialized
157 attr
.kern_version
= kern_version
;
159 long res
= syscall(SYS_bpf
, BPF_PROG_LOAD
, &attr
, sizeof(attr
));
161 if (errno
== ENOSPC
) {
162 /* not enough space in the log buffer */
165 attr
.log_buf
= ptr_to_u64(nullptr);
166 res
= syscall(SYS_bpf
, BPF_PROG_LOAD
, &attr
, sizeof(attr
));
171 throw std::runtime_error("Error loading BPF program: (" + stringerror() + "):\n" + std::string(log_buf
));
186 struct QNameAndQTypeKey
199 BPFFilter::Map::Map(const BPFFilter::MapConfiguration
& config
, BPFFilter::MapFormat format
): d_config(config
)
201 if (d_config
.d_type
== BPFFilter::MapType::Filters
) {
202 /* special case, this is a map of eBPF programs */
203 d_fd
= FDWrapper(bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY
, sizeof(uint32_t), sizeof(uint32_t), d_config
.d_maxItems
, 0));
204 if (d_fd
.getHandle() == -1) {
205 throw std::runtime_error("Error creating a BPF program map of size " + std::to_string(d_config
.d_maxItems
) + ": " + stringerror());
212 bpf_map_type type
= BPF_MAP_TYPE_HASH
;
213 if (format
== MapFormat::Legacy
) {
214 switch (d_config
.d_type
) {
216 keySize
= sizeof(uint32_t);
217 valueSize
= sizeof(uint64_t);
220 keySize
= sizeof(KeyV6
);
221 valueSize
= sizeof(uint64_t);
223 case MapType::QNames
:
224 keySize
= sizeof(QNameKey
);
225 valueSize
= sizeof(QNameValue
);
228 throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast<uint8_t>(d_config
.d_type
)) + " for legacy eBPF, perhaps you are trying to use an external program instead?");
232 switch (d_config
.d_type
) {
234 keySize
= sizeof(uint32_t);
235 valueSize
= sizeof(CounterAndActionValue
);
238 keySize
= sizeof(KeyV6
);
239 valueSize
= sizeof(CounterAndActionValue
);
242 keySize
= sizeof(CIDR4
);
243 valueSize
= sizeof(CounterAndActionValue
);
244 flags
= BPF_F_NO_PREALLOC
;
245 type
= BPF_MAP_TYPE_LPM_TRIE
;
248 keySize
= sizeof(CIDR6
);
249 valueSize
= sizeof(CounterAndActionValue
);
250 flags
= BPF_F_NO_PREALLOC
;
251 type
= BPF_MAP_TYPE_LPM_TRIE
;
253 case MapType::QNames
:
254 keySize
= sizeof(QNameAndQTypeKey
);
255 valueSize
= sizeof(CounterAndActionValue
);
258 throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast<uint8_t>(d_config
.d_type
)));
262 if (!d_config
.d_pinnedPath
.empty()) {
264 d_fd
= FDWrapper(bpf_load_pinned_map(d_config
.d_pinnedPath
));
265 if (d_fd
.getHandle() != -1) {
266 /* sanity checks: key and value size */
267 bpf_check_map_sizes(d_fd
.getHandle(), keySize
, valueSize
);
268 switch (d_config
.d_type
) {
269 case MapType::IPv4
: {
271 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
276 case MapType::IPv6
: {
278 memset(&key
, 0, sizeof(key
));
279 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
284 case MapType::CIDR4
: {
286 memset(&key
, 0, sizeof(key
));
287 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
292 case MapType::CIDR6
: {
294 memset(&key
, 0, sizeof(key
));
295 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
300 case MapType::QNames
: {
301 if (format
== MapFormat::Legacy
) {
303 memset(&key
, 0, sizeof(key
));
304 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
309 QNameAndQTypeKey key
;
310 memset(&key
, 0, sizeof(key
));
311 while (bpf_get_next_key(d_fd
.getHandle(), &key
, &key
) == 0) {
319 throw std::runtime_error("Unsupported eBPF map type: " + std::to_string(static_cast<uint8_t>(d_config
.d_type
)));
324 if (d_fd
.getHandle() == -1) {
325 d_fd
= FDWrapper(bpf_create_map(type
, keySize
, valueSize
, static_cast<int>(d_config
.d_maxItems
), flags
));
326 if (d_fd
.getHandle() == -1) {
327 throw std::runtime_error("Error creating a BPF map of size " + std::to_string(d_config
.d_maxItems
) + ": " + stringerror());
330 if (!d_config
.d_pinnedPath
.empty()) {
331 if (bpf_pin_map(d_fd
.getHandle(), d_config
.d_pinnedPath
) != 0) {
332 throw std::runtime_error("Unable to pin map to path '" + d_config
.d_pinnedPath
+ "': " + stringerror());
339 static FDWrapper
loadProgram(const struct bpf_insn
* filter
, size_t filterSize
)
341 auto descriptor
= FDWrapper(bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER
,
346 if (descriptor
.getHandle() == -1) {
347 throw std::runtime_error("error loading BPF filter: " + stringerror());
353 BPFFilter::BPFFilter(std::unordered_map
<std::string
, MapConfiguration
>& configs
, BPFFilter::MapFormat format
, bool external
) :
354 d_mapFormat(format
), d_external(external
)
356 if (d_mapFormat
!= BPFFilter::MapFormat::Legacy
&& !d_external
) {
357 throw std::runtime_error("Unsupported eBPF map format, the current internal implemenation only supports the legacy format");
360 struct rlimit old_limit
;
361 if (getrlimit(RLIMIT_MEMLOCK
, &old_limit
) != 0) {
362 throw std::runtime_error("Unable to get memory lock limit: " + stringerror());
365 const rlim_t new_limit_size
= 1024 * 1024;
367 /* Check if the current soft memlock limit is at least the limit */
368 if (old_limit
.rlim_cur
< new_limit_size
) {
369 infolog("The current limit of locked memory (soft: %d, hard: %d) is too low for eBPF, trying to raise it to %d", old_limit
.rlim_cur
, old_limit
.rlim_max
, new_limit_size
);
371 struct rlimit new_limit
;
372 new_limit
.rlim_cur
= new_limit_size
;
373 new_limit
.rlim_max
= new_limit_size
;
375 if (setrlimit(RLIMIT_MEMLOCK
, &new_limit
) != 0) {
376 warnlog("Unable to raise the maximum amount of locked memory for eBPF from %d to %d, consider raising RLIMIT_MEMLOCK or setting LimitMEMLOCK in the systemd unit: %d", old_limit
.rlim_cur
, new_limit
.rlim_cur
, stringerror());
380 auto maps
= d_maps
.lock();
382 maps
->d_v4
= BPFFilter::Map(configs
["ipv4"], d_mapFormat
);
383 maps
->d_v6
= BPFFilter::Map(configs
["ipv6"], d_mapFormat
);
384 maps
->d_qnames
= BPFFilter::Map(configs
["qnames"], d_mapFormat
);
386 if (d_mapFormat
!= BPFFilter::MapFormat::Legacy
) {
387 maps
->d_cidr4
= BPFFilter::Map(configs
["cidr4"], d_mapFormat
);
388 maps
->d_cidr6
= BPFFilter::Map(configs
["cidr6"], d_mapFormat
);
392 BPFFilter::MapConfiguration filters
;
393 filters
.d_maxItems
= 1;
394 filters
.d_type
= BPFFilter::MapType::Filters
;
395 maps
->d_filters
= BPFFilter::Map(filters
, d_mapFormat
);
397 const struct bpf_insn main_filter
[] = {
398 #include "bpf-filter.main.ebpf"
401 const struct bpf_insn qname_filter
[] = {
402 #include "bpf-filter.qname.ebpf"
406 d_mainfilter
= loadProgram(main_filter
,
407 sizeof(main_filter
));
409 catch (const std::exception
& e
) {
410 throw std::runtime_error("Error load the main eBPF filter: " + std::string(e
.what()));
414 d_qnamefilter
= loadProgram(qname_filter
,
415 sizeof(qname_filter
));
417 catch (const std::exception
& e
) {
418 throw std::runtime_error("Error load the qname eBPF filter: " + std::string(e
.what()));
422 int qnamefd
= d_qnamefilter
.getHandle();
423 int res
= bpf_update_elem(maps
->d_filters
.d_fd
.getHandle(), &key
, &qnamefd
, BPF_ANY
);
425 throw std::runtime_error("Error updating BPF filters map: " + stringerror());
430 void BPFFilter::addSocket(int sock
)
432 int descriptor
= d_mainfilter
.getHandle();
433 int res
= setsockopt(sock
, SOL_SOCKET
, SO_ATTACH_BPF
, &descriptor
, sizeof(descriptor
));
436 throw std::runtime_error("Error attaching BPF filter to this socket: " + stringerror());
440 void BPFFilter::removeSocket(int sock
)
442 int descriptor
= d_mainfilter
.getHandle();
443 int res
= setsockopt(sock
, SOL_SOCKET
, SO_DETACH_BPF
, &descriptor
, sizeof(descriptor
));
446 throw std::runtime_error("Error detaching BPF filter from this socket: " + stringerror());
450 void BPFFilter::block(const ComboAddress
& addr
, BPFFilter::MatchAction action
)
452 CounterAndActionValue value
;
454 value
.action
= action
;
458 uint32_t key
= htonl(addr
.sin4
.sin_addr
.s_addr
);
459 auto maps
= d_maps
.lock();
460 auto& map
= maps
->d_v4
;
461 if (map
.d_count
>= map
.d_config
.d_maxItems
) {
462 throw std::runtime_error("Table full when trying to block " + addr
.toString());
465 res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &value
);
467 throw std::runtime_error("Trying to block an already blocked address: " + addr
.toString());
470 res
= bpf_update_elem(map
.d_fd
.getHandle(), &key
, &value
, BPF_NOEXIST
);
475 else if (addr
.isIPv6()) {
477 static_assert(sizeof(addr
.sin6
.sin6_addr
.s6_addr
) == sizeof(key
), "POSIX mandates s6_addr to be an array of 16 uint8_t");
478 for (size_t idx
= 0; idx
< sizeof(key
); idx
++) {
479 key
[idx
] = addr
.sin6
.sin6_addr
.s6_addr
[idx
];
482 auto maps
= d_maps
.lock();
483 auto& map
= maps
->d_v6
;
484 if (map
.d_count
>= map
.d_config
.d_maxItems
) {
485 throw std::runtime_error("Table full when trying to block " + addr
.toString());
488 res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &value
);
490 throw std::runtime_error("Trying to block an already blocked address: " + addr
.toString());
493 res
= bpf_update_elem(map
.d_fd
.getHandle(), key
, &value
, BPF_NOEXIST
);
500 throw std::runtime_error("Error adding blocked address " + addr
.toString() + ": " + stringerror());
504 void BPFFilter::unblock(const ComboAddress
& addr
)
508 uint32_t key
= htonl(addr
.sin4
.sin_addr
.s_addr
);
509 auto maps
= d_maps
.lock();
510 auto& map
= maps
->d_v4
;
511 res
= bpf_delete_elem(map
.d_fd
.getHandle(), &key
);
516 else if (addr
.isIPv6()) {
518 static_assert(sizeof(addr
.sin6
.sin6_addr
.s6_addr
) == sizeof(key
), "POSIX mandates s6_addr to be an array of 16 uint8_t");
519 for (size_t idx
= 0; idx
< sizeof(key
); idx
++) {
520 key
[idx
] = addr
.sin6
.sin6_addr
.s6_addr
[idx
];
523 auto maps
= d_maps
.lock();
524 auto& map
= maps
->d_v6
;
525 res
= bpf_delete_elem(map
.d_fd
.getHandle(), key
);
532 throw std::runtime_error("Error removing blocked address " + addr
.toString() + ": " + stringerror());
536 void BPFFilter::addRangeRule(const Netmask
& addr
, bool force
, BPFFilter::MatchAction action
)
538 CounterAndActionValue value
;
543 auto maps
= d_maps
.lock();
544 auto& map
= maps
->d_cidr4
;
545 if (map
.d_fd
.getHandle() == -1) {
546 throw std::runtime_error("Trying to use an unsupported map type, likely adding a range to a legacy eBPF program");
548 if (map
.d_count
>= map
.d_config
.d_maxItems
) {
549 throw std::runtime_error("Table full when trying to add this rule: " + addr
.toString());
552 res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &value
);
553 if (((res
!= -1 && value
.action
== action
) || (res
== -1 && value
.action
== BPFFilter::MatchAction::Pass
)) && !force
) {
554 throw std::runtime_error("Trying to add a useless rule: " + addr
.toString());
558 value
.action
= action
;
560 res
= bpf_update_elem(map
.d_fd
.getHandle(), &key
, &value
, force
? BPF_ANY
: BPF_NOEXIST
);
565 else if (addr
.isIPv6()) {
568 auto maps
= d_maps
.lock();
569 auto& map
= maps
->d_cidr6
;
570 if (map
.d_fd
.getHandle() == -1) {
571 throw std::runtime_error("Trying to use an unsupported map type, likely adding a range to a legacy eBPF program");
573 if (map
.d_count
>= map
.d_config
.d_maxItems
) {
574 throw std::runtime_error("Table full when trying to add this rule: " + addr
.toString());
577 res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &value
);
578 if (((res
!= -1 && value
.action
== action
) || (res
== -1 && value
.action
== BPFFilter::MatchAction::Pass
)) && !force
) {
579 throw std::runtime_error("Trying to add a useless rule: " + addr
.toString());
583 value
.action
= action
;
585 res
= bpf_update_elem(map
.d_fd
.getHandle(), &key
, &value
, BPF_NOEXIST
);
592 throw std::runtime_error("Error adding this rule: " + addr
.toString() + ": " + stringerror());
596 void BPFFilter::rmRangeRule(const Netmask
& addr
)
599 CounterAndActionValue value
;
601 value
.action
= MatchAction::Pass
;
604 auto maps
= d_maps
.lock();
605 auto& map
= maps
->d_cidr4
;
606 if (map
.d_fd
.getHandle() == -1) {
607 throw std::runtime_error("Trying to use an unsupported map type, likely adding a range to a legacy eBPF program");
609 res
= bpf_delete_elem(map
.d_fd
.getHandle(), &key
);
614 throw std::runtime_error("Cannot remove '" + addr
.toString() + "': No such rule");
617 else if (addr
.isIPv6()) {
620 auto maps
= d_maps
.lock();
621 auto& map
= maps
->d_cidr6
;
622 if (map
.d_fd
.getHandle() == -1) {
623 throw std::runtime_error("Trying to use an unsupported map type, likely adding a range to a legacy eBPF program");
625 res
= bpf_delete_elem(map
.d_fd
.getHandle(), &key
);
630 throw std::runtime_error("Cannot remove '" + addr
.toString() + "': No such rule");
635 throw std::runtime_error("Error removing this rule: " + addr
.toString() + ": " + stringerror());
639 void BPFFilter::block(const DNSName
& qname
, BPFFilter::MatchAction action
, uint16_t qtype
)
641 CounterAndActionValue cadvalue
;
643 void* value
= nullptr;
646 cadvalue
.counter
= 0;
647 cadvalue
.action
= action
;
652 qvalue
.qtype
= qtype
;
656 QNameAndQTypeKey key
;
657 memset(&key
, 0, sizeof(key
));
659 std::string keyStr
= qname
.toDNSStringLC();
660 if (keyStr
.size() > sizeof(key
.qname
)) {
661 throw std::runtime_error("Invalid QName to block " + qname
.toLogString());
663 memcpy(key
.qname
, keyStr
.c_str(), keyStr
.size());
667 auto maps
= d_maps
.lock();
668 auto& map
= maps
->d_qnames
;
669 if (map
.d_count
>= map
.d_config
.d_maxItems
) {
670 throw std::runtime_error("Table full when trying to block " + qname
.toLogString());
673 int res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, value
);
675 throw std::runtime_error("Trying to block an already blocked qname: " + qname
.toLogString());
677 res
= bpf_update_elem(map
.d_fd
.getHandle(), &key
, value
, BPF_NOEXIST
);
683 throw std::runtime_error("Error adding blocked qname " + qname
.toLogString() + ": " + stringerror());
688 void BPFFilter::unblock(const DNSName
& qname
, uint16_t qtype
)
690 QNameAndQTypeKey key
;
691 memset(&key
, 0, sizeof(key
));
692 std::string keyStr
= qname
.toDNSStringLC();
694 if (keyStr
.size() > sizeof(key
.qname
)) {
695 throw std::runtime_error("Invalid QName to block " + qname
.toLogString());
697 memcpy(key
.qname
, keyStr
.c_str(), keyStr
.size());
701 auto maps
= d_maps
.lock();
702 auto& map
= maps
->d_qnames
;
703 int res
= bpf_delete_elem(map
.d_fd
.getHandle(), &key
);
708 throw std::runtime_error("Error removing qname address " + qname
.toLogString() + ": " + stringerror());
713 std::vector
<std::pair
<ComboAddress
, uint64_t> > BPFFilter::getAddrStats()
715 std::vector
<std::pair
<ComboAddress
, uint64_t> > result
;
717 auto maps
= d_maps
.lock();
718 result
.reserve(maps
->d_v4
.d_count
+ maps
->d_v6
.d_count
);
721 sockaddr_in v4Addr
{};
722 memset(&v4Addr
, 0, sizeof(v4Addr
));
723 v4Addr
.sin_family
= AF_INET
;
726 uint32_t nextV4Key
{};
727 CounterAndActionValue value
{};
729 std::array
<uint8_t, 16> v6Key
{};
730 std::array
<uint8_t, 16> nextV6Key
{};
731 sockaddr_in6 v6Addr
{};
732 memset(&v6Addr
, 0, sizeof(v6Addr
));
733 v6Addr
.sin6_family
= AF_INET6
;
735 static_assert(sizeof(v6Addr
.sin6_addr
.s6_addr
) == v6Key
.size(), "POSIX mandates s6_addr to be an array of 16 uint8_t");
736 memset(&v6Key
, 0, sizeof(v6Key
));
738 auto maps
= d_maps
.lock();
741 auto& map
= maps
->d_v4
;
742 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), &v4Key
, &nextV4Key
);
746 if (bpf_lookup_elem(map
.d_fd
.getHandle(), &v4Key
, &value
) == 0) {
747 v4Addr
.sin_addr
.s_addr
= ntohl(v4Key
);
748 result
.emplace_back(ComboAddress(&v4Addr
), value
.counter
);
751 res
= bpf_get_next_key(map
.d_fd
.getHandle(), &v4Key
, &nextV4Key
);
756 auto& map
= maps
->d_v6
;
757 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), v6Key
.data(), nextV6Key
.data());
760 if (bpf_lookup_elem(map
.d_fd
.getHandle(), nextV6Key
.data(), &value
) == 0) {
761 memcpy(&v6Addr
.sin6_addr
.s6_addr
, nextV6Key
.data(), nextV6Key
.size());
763 result
.emplace_back(ComboAddress(&v6Addr
), value
.counter
);
766 res
= bpf_get_next_key(map
.d_fd
.getHandle(), nextV6Key
.data(), nextV6Key
.data());
773 std::vector
<std::pair
<Netmask
, CounterAndActionValue
>> BPFFilter::getRangeRule()
777 std::vector
<std::pair
<Netmask
, CounterAndActionValue
>> result
;
781 CounterAndActionValue value
;
783 memset(cidr4
, 0, sizeof(cidr4
));
784 memset(cidr6
, 0, sizeof(cidr6
));
785 memset(&v4Addr
, 0, sizeof(v4Addr
));
786 memset(&v6Addr
, 0, sizeof(v6Addr
));
787 v4Addr
.sin_family
= AF_INET
;
788 v6Addr
.sin6_family
= AF_INET6
;
789 auto maps
= d_maps
.lock();
790 result
.reserve(maps
->d_cidr4
.d_count
+ maps
->d_cidr6
.d_count
);
792 auto& map
= maps
->d_cidr4
;
793 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), &cidr4
[0], &cidr4
[1]);
795 if (bpf_lookup_elem(map
.d_fd
.getHandle(), &cidr4
[1], &value
) == 0) {
796 v4Addr
.sin_addr
.s_addr
= cidr4
[1].addr
.s_addr
;
797 result
.emplace_back(Netmask(&v4Addr
, cidr4
[1].cidr
), value
);
800 res
= bpf_get_next_key(map
.d_fd
.getHandle(), &cidr4
[1], &cidr4
[1]);
805 auto& map
= maps
->d_cidr6
;
806 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), &cidr6
[0], &cidr6
[1]);
808 if (bpf_lookup_elem(map
.d_fd
.getHandle(), &cidr6
[1], &value
) == 0) {
809 v6Addr
.sin6_addr
= cidr6
[1].addr
;
810 result
.emplace_back(Netmask(&v6Addr
, cidr6
[1].cidr
), value
);
813 res
= bpf_get_next_key(map
.d_fd
.getHandle(), &cidr6
[1], &cidr6
[1]);
819 std::vector
<std::tuple
<DNSName
, uint16_t, uint64_t> > BPFFilter::getQNameStats()
821 std::vector
<std::tuple
<DNSName
, uint16_t, uint64_t> > result
;
823 if (d_mapFormat
== MapFormat::Legacy
) {
824 QNameKey key
= { { 0 } };
825 QNameKey nextKey
= { { 0 } };
828 auto maps
= d_maps
.lock();
829 auto& map
= maps
->d_qnames
;
830 result
.reserve(map
.d_count
);
831 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), &key
, &nextKey
);
834 if (bpf_lookup_elem(map
.d_fd
.getHandle(), &nextKey
, &value
) == 0) {
835 nextKey
.qname
[sizeof(nextKey
.qname
) - 1 ] = '\0';
836 result
.emplace_back(DNSName(reinterpret_cast<const char*>(nextKey
.qname
), sizeof(nextKey
.qname
), 0, false), value
.qtype
, value
.counter
);
839 res
= bpf_get_next_key(map
.d_fd
.getHandle(), &nextKey
, &nextKey
);
843 QNameAndQTypeKey key
;
844 QNameAndQTypeKey nextKey
;
845 memset(&key
, 0, sizeof(key
));
846 memset(&nextKey
, 0, sizeof(nextKey
));
847 CounterAndActionValue value
;
849 auto maps
= d_maps
.lock();
850 auto& map
= maps
->d_qnames
;
851 result
.reserve(map
.d_count
);
852 int res
= bpf_get_next_key(map
.d_fd
.getHandle(), &key
, &nextKey
);
855 if (bpf_lookup_elem(map
.d_fd
.getHandle(), &nextKey
, &value
) == 0) {
856 nextKey
.qname
[sizeof(nextKey
.qname
) - 1 ] = '\0';
857 result
.emplace_back(DNSName(reinterpret_cast<const char*>(nextKey
.qname
), sizeof(nextKey
.qname
), 0, false), key
.qtype
, value
.counter
);
860 res
= bpf_get_next_key(map
.d_fd
.getHandle(), &nextKey
, &nextKey
);
867 uint64_t BPFFilter::getHits(const ComboAddress
& requestor
)
869 CounterAndActionValue counter
;
871 if (requestor
.isIPv4()) {
872 uint32_t key
= htonl(requestor
.sin4
.sin_addr
.s_addr
);
874 auto maps
= d_maps
.lock();
875 auto& map
= maps
->d_v4
;
876 int res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &counter
);
878 return counter
.counter
;
881 else if (requestor
.isIPv6()) {
883 static_assert(sizeof(requestor
.sin6
.sin6_addr
.s6_addr
) == sizeof(key
), "POSIX mandates s6_addr to be an array of 16 uint8_t");
884 for (size_t idx
= 0; idx
< sizeof(key
); idx
++) {
885 key
[idx
] = requestor
.sin6
.sin6_addr
.s6_addr
[idx
];
888 auto maps
= d_maps
.lock();
889 auto& map
= maps
->d_v6
;
890 int res
= bpf_lookup_elem(map
.d_fd
.getHandle(), &key
, &counter
);
892 return counter
.counter
;
901 BPFFilter::BPFFilter(std::unordered_map
<std::string
, MapConfiguration
>& configs
, BPFFilter::MapFormat format
, bool external
)
905 void BPFFilter::addSocket(int)
907 throw std::runtime_error("eBPF support not enabled");
910 void BPFFilter::removeSocket(int)
912 throw std::runtime_error("eBPF support not enabled");
915 void BPFFilter::block(const ComboAddress
&, BPFFilter::MatchAction
)
917 throw std::runtime_error("eBPF support not enabled");
920 void BPFFilter::unblock(const ComboAddress
&)
922 throw std::runtime_error("eBPF support not enabled");
925 void BPFFilter::block(const DNSName
&, BPFFilter::MatchAction
, uint16_t)
927 throw std::runtime_error("eBPF support not enabled");
930 void BPFFilter::unblock(const DNSName
&, uint16_t)
932 throw std::runtime_error("eBPF support not enabled");
935 void BPFFilter::addRangeRule(const Netmask
&, bool, BPFFilter::MatchAction
)
937 throw std::runtime_error("eBPF support not enabled");
939 void BPFFilter::rmRangeRule(const Netmask
&)
941 throw std::runtime_error("eBPF support not enabled");
944 std::vector
<std::pair
<Netmask
, CounterAndActionValue
>> BPFFilter::getRangeRule(){
945 std::vector
<std::pair
<Netmask
, CounterAndActionValue
>> result
;
948 std::vector
<std::pair
<ComboAddress
, uint64_t> > BPFFilter::getAddrStats()
950 std::vector
<std::pair
<ComboAddress
, uint64_t> > result
;
954 std::vector
<std::tuple
<DNSName
, uint16_t, uint64_t> > BPFFilter::getQNameStats()
956 std::vector
<std::tuple
<DNSName
, uint16_t, uint64_t> > result
;
960 uint64_t BPFFilter::getHits(const ComboAddress
&)
964 #endif /* HAVE_EBPF */
966 bool BPFFilter::supportsMatchAction(MatchAction action
) const
969 if (action
== BPFFilter::MatchAction::Drop
) {
972 return d_mapFormat
== BPFFilter::MapFormat::WithActions
;
973 #endif /* HAVE_EBPF */
977 bool BPFFilter::isExternal() const
981 #endif /* HAVE_EBPF */