ebpf-blocker 1.0.0
XDP-based packet blocker using eBPF
Loading...
Searching...
No Matches
xdp_loader.cpp
Go to the documentation of this file.
1/// \file xdp_loader.cpp
2/// \brief Загрузка BPF-объекта, прикрепление/открепление XDP и управление
3/// картами. \details Содержит реализацию XdpBlocker: загрузка из встроенного
4/// байткода,
5/// прикрепление/открепление XDP, очистка и обновление BPF-карт.
6
7#include "xdp_loader.h"
8
9#include <bpf/bpf.h>
10#include <bpf/libbpf.h>
11#include <cstdarg>
12#include <cstring>
13#include <iostream>
14#include <net/if.h>
15#include <stdexcept>
16
17/// Встроенный BPF-байткод, сгенерированный xxd во время сборки.
18extern "C" {
19extern unsigned char blocker_bpf[];
20extern unsigned int blocker_bpf_len;
21}
22
23/// Колбэк логирования libbpf: передаёт только предупреждения в stderr.
24/// @param[in] lvl Уровень логирования.
25/// @param[in] msg Сообщение.
26/// @return 0.
27static int PrintLibbpfLog(enum libbpf_print_level lvl, const char *msg,
28 va_list /*args*/) {
29 if (lvl == LIBBPF_WARN)
30 std::cerr << "libbpf: " << msg;
31 return 0;
32}
33
34/**********************************************************************/
35/* Constructor / destructor */
36/**********************************************************************/
37
39 : obj_(nullptr), whitelist_map_(nullptr), blacklist_map_(nullptr),
40 drop_counter_map_(nullptr) {
41 libbpf_set_print(PrintLibbpfLog);
42}
43
45
46/**********************************************************************/
47/* Load / Unload */
48/**********************************************************************/
49
50void XdpBlocker::Load(const std::string &iface) {
51 iface_ = iface;
54}
55
57 if (iface_.empty())
58 return;
60 if (obj_) {
61 bpf_object__close(obj_);
62 obj_ = nullptr;
63 whitelist_map_ = nullptr;
64 blacklist_map_ = nullptr;
65 drop_counter_map_ = nullptr;
66 }
67 iface_.clear();
68}
69
70/**********************************************************************/
71/* BPF program loading */
72/**********************************************************************/
73
74/// @brief Открывает и загружает BPF-объект из встроенного массива байткода.
76 if (blocker_bpf_len == 0)
77 throw std::runtime_error("BPF object is empty");
78
79 struct bpf_object_open_opts opts = {};
80 opts.sz = sizeof(opts);
81 obj_ = bpf_object__open_mem(blocker_bpf, blocker_bpf_len, &opts);
82 if (!obj_)
83 throw std::runtime_error("failed to open BPF object from memory");
84
85 if (bpf_object__load(obj_))
86 throw std::runtime_error("failed to load BPF object into kernel");
87
88 whitelist_map_ = bpf_object__find_map_by_name(obj_, "whitelist_map");
89 if (!whitelist_map_)
90 throw std::runtime_error("whitelist_map not found in BPF object");
91
92 blacklist_map_ = bpf_object__find_map_by_name(obj_, "blacklist_map");
93 if (!blacklist_map_)
94 throw std::runtime_error("blacklist_map not found in BPF object");
95
96 drop_counter_map_ = bpf_object__find_map_by_name(obj_, "drop_counter");
98 throw std::runtime_error("drop_counter not found in BPF object");
99}
100
101/**********************************************************************/
102/* Interface attach / detach */
103/**********************************************************************/
104
105/// @brief Прикрепляет XDP-программу к сетевому интерфейсу.
107 bpf_program *prog = bpf_object__next_program(obj_, nullptr);
108 if (!prog)
109 throw std::runtime_error("no BPF program found in object");
110
111 bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
112
113 int ifindex = if_nametoindex(iface_.c_str());
114 if (ifindex == 0)
115 throw std::runtime_error("interface not found: " + iface_);
116
117 int fd = bpf_program__fd(prog);
118 if (fd < 0)
119 throw std::runtime_error("invalid program fd");
120
121 struct bpf_xdp_attach_opts attach_opts = {};
122 attach_opts.sz = sizeof(attach_opts);
123
124 if (bpf_xdp_attach(ifindex, fd, 0, &attach_opts))
125 throw std::runtime_error("failed to attach XDP program to " + iface_);
126}
127
128/// @brief Открепляет XDP-программу от сетевого интерфейса.
130 int ifindex = if_nametoindex(iface_.c_str());
131 if (ifindex == 0)
132 return;
133
134 struct bpf_xdp_attach_opts detach_opts = {};
135 detach_opts.sz = sizeof(detach_opts);
136
137 bpf_xdp_detach(ifindex, 0, &detach_opts);
138}
139
140/**********************************************************************/
141/* Map management */
142/**********************************************************************/
143
144/// @brief Перебирает и удаляет все ключи из BPF-карты (итерация LPM trie).
145void XdpBlocker::ClearMap(bpf_map *map) {
146 int fd = bpf_map__fd(map);
147 if (fd < 0)
148 return;
149
150 struct lpm_key {
151 __u32 prefixlen;
152 __u32 ip;
153 } next_key = {};
154 struct lpm_key *cur_key = nullptr;
155
156 while (bpf_map_get_next_key(fd, cur_key, &next_key) == 0) {
157 bpf_map_delete_elem(fd, &next_key);
158 cur_key = &next_key;
159 }
160}
161
162/// @brief Заменяет все записи в указанной BPF-карте.
163///
164/// Сначала очищает карту, затем вставляет каждую корректную запись BlockEntry.
165void XdpBlocker::UpdateMap(const std::string &map_name,
166 const std::vector<BlockEntry> &entries) {
167 bpf_map *map = nullptr;
168 if (map_name == "whitelist_map")
169 map = whitelist_map_;
170 else if (map_name == "blacklist_map")
171 map = blacklist_map_;
172 else
173 throw std::invalid_argument("unknown map: " + map_name);
174
175 ClearMap(map);
176
177 int fd = bpf_map__fd(map);
178 if (fd < 0)
179 throw std::runtime_error("invalid fd for " + map_name);
180
181 __u32 value = (map_name == "whitelist_map") ? 0 : 1;
182
183 for (const auto &entry : entries) {
184 try {
185 CIDR cidr = ParseCIDR(entry.ip);
186 struct {
187 __u32 prefixlen;
188 __u32 ip;
189 } key = {cidr.prefixlen, cidr.ip};
190
191 if (bpf_map_update_elem(fd, &key, &value, BPF_ANY))
192 std::cerr << "warning: failed to add " << entry.ip << " to " << map_name
193 << std::endl;
194 } catch (const std::exception &e) {
195 std::cerr << "warning: skipping invalid entry '" << entry.ip
196 << "': " << e.what() << std::endl;
197 }
198 }
199}
200
201void XdpBlocker::UpdateWhitelist(const std::vector<BlockEntry> &entries) {
202 UpdateMap("whitelist_map", entries);
203}
204
205void XdpBlocker::UpdateBlacklist(const std::vector<BlockEntry> &entries) {
206 UpdateMap("blacklist_map", entries);
207}
208
209/**********************************************************************/
210/* Drop counter reading */
211/**********************************************************************/
212
213/// @brief Читает все записи из карты drop_counter и атомарно очищает их.
214std::vector<std::pair<uint32_t, uint64_t>>
216 std::vector<std::pair<uint32_t, uint64_t>> result;
217
218 int fd = bpf_map__fd(drop_counter_map_);
219 if (fd < 0)
220 return result;
221
222 __u32 *cur_key = nullptr;
223 __u32 next_key = 0;
224 __u64 value = 0;
225
226 while (bpf_map_get_next_key(fd, cur_key, &next_key) == 0) {
227 if (bpf_map_lookup_elem(fd, &next_key, &value) == 0 && value > 0)
228 result.emplace_back(next_key, value);
229 bpf_map_delete_elem(fd, &next_key);
230 cur_key = &next_key;
231 }
232
233 return result;
234}
void DetachFromInterface()
Открепляет XDP-программу от iface_.
void AttachToInterface()
Прикрепляет загруженную XDP-программу к iface_.
std::vector< std::pair< uint32_t, uint64_t > > ReadAndClearDropCounters()
Читает все записи из карты drop_counter и атомарно очищает их.
void Unload()
Открепляет XDP и закрывает BPF-объект. Безопасен для многократного вызова.
void LoadBPFProgram()
Открывает и загружает BPF-объект из встроенного массива байткода.
bpf_object * obj_
Дескриптор BPF-объекта libbpf.
Definition xdp_loader.h:64
std::string iface_
Имя текущего прикреплённого интерфейса.
Definition xdp_loader.h:68
bpf_map * whitelist_map_
LPM trie для записей белого списка.
Definition xdp_loader.h:65
void UpdateWhitelist(const std::vector< BlockEntry > &entries)
void UpdateBlacklist(const std::vector< BlockEntry > &entries)
void ClearMap(bpf_map *map)
Перебирает и удаляет все ключи из BPF-карты (итерация LPM trie).
bpf_map * drop_counter_map_
Хэш-карта для счётчиков дропов по IP.
Definition xdp_loader.h:67
bpf_map * blacklist_map_
LPM trie для записей чёрного списка.
Definition xdp_loader.h:66
void UpdateMap(const std::string &map_name, const std::vector< BlockEntry > &entries)
Заменяет все записи в указанной BPF-карте.
void Load(const std::string &iface)
CIDR ParseCIDR(const std::string &str)
Definition db.cpp:18
Распарсенное представление CIDR-подсети.
Definition db.h:21
uint32_t prefixlen
Длина префикса (0-32). 32 означает одиночный хост.
Definition db.h:23
uint32_t ip
IP-адрес в network byte order с применённой маской.
Definition db.h:22
Ключ для LPM trie-карт: длина префикса + IP в network byte order.
static int PrintLibbpfLog(enum libbpf_print_level lvl, const char *msg, va_list)
unsigned int blocker_bpf_len
unsigned char blocker_bpf[]
Встроенный BPF-байткод, сгенерированный xxd во время сборки.
Загрузчик BPF-объекта и менеджер XDP-программы.