Skip to content

Commit 1065a42

Browse files
committed
n-acd: runtime eBPF support detection
Currently, eBPF support is toggled by a compile-time flag based on whether we expect the system to support it or not. Make it so that n-acd always attempts to use eBPF, and gracefully handles failure if it isn't supported (e.g. due to lacking capabilities).
1 parent a600afc commit 1065a42

10 files changed

+75
-64
lines changed

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,6 @@ meson test
4141
ninja install
4242
```
4343

44-
The following configuration options are available:
45-
46-
* `ebpf`: This boolean controls whether `ebpf` features are used to improve
47-
the package filtering performance. If disabled, classic bpf will be
48-
used. This feature requires a rather recent kernel (>=3.19).
49-
Default is: true
50-
5144
### Repository:
5245

5346
- **web**: <https://github.com/nettools/n-acd>

meson.build

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,4 @@ dep_crbtree = sub_crbtree.get_variable('libcrbtree_dep')
2222
dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
2323
dep_cstdaux = sub_cstdaux.get_variable('libcstdaux_dep')
2424

25-
use_ebpf = get_option('ebpf')
26-
2725
subdir('src')

src/libnacd.sym

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ global:
1515
n_acd_ref;
1616
n_acd_unref;
1717
n_acd_get_fd;
18+
n_acd_has_bpf;
1819
n_acd_dispatch;
1920
n_acd_pop_event;
2021
n_acd_probe;

src/meson.build

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,9 @@ libnacd_sources = [
1515
'n-acd.c',
1616
'n-acd-probe.c',
1717
'util/timer.c',
18+
'n-acd-bpf.c',
1819
]
1920

20-
if use_ebpf
21-
libnacd_sources += [
22-
'n-acd-bpf.c',
23-
]
24-
else
25-
libnacd_sources += [
26-
'n-acd-bpf-fallback.c',
27-
]
28-
endif
29-
3021
libnacd_private = static_library(
3122
'nacd-private',
3223
libnacd_sources,
@@ -77,10 +68,8 @@ endif
7768
test_api = executable('test-api', ['test-api.c'], link_with: libnacd_shared)
7869
test('API Symbol Visibility', test_api)
7970

80-
if use_ebpf
81-
test_bpf = executable('test-bpf', ['test-bpf.c'], dependencies: libnacd_dep)
82-
test('eBPF socket filtering', test_bpf)
83-
endif
71+
test_bpf = executable('test-bpf', ['test-bpf.c'], dependencies: libnacd_dep)
72+
test('eBPF socket filtering', test_bpf)
8473

8574
test_loopback = executable('test-loopback', ['test-loopback.c'], dependencies: libnacd_dep)
8675
test('Echo Suppression via Loopback', test_loopback)

src/n-acd-bpf-fallback.c

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/n-acd-bpf.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ int n_acd_bpf_map_add(int mapfd, struct in_addr *addrp) {
170170
uint8_t _dummy = 0;
171171
int r;
172172

173+
/* If we don't have a map to update, there is nothing to do. */
174+
if (mapfd == -1)
175+
return 0;
176+
173177
memset(&attr, 0, sizeof(attr));
174178
attr = (union bpf_attr){
175179
.map_fd = mapfd,
@@ -190,6 +194,10 @@ int n_acd_bpf_map_remove(int mapfd, struct in_addr *addrp) {
190194
union bpf_attr attr;
191195
int r;
192196

197+
/* If we don't have a map to update, there is nothing to do. */
198+
if (mapfd == -1)
199+
return 0;
200+
193201
memset(&attr, 0, sizeof(attr));
194202
attr = (union bpf_attr){
195203
.map_fd = mapfd,

src/n-acd.c

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,22 @@ int n_acd_ensure_bpf_map_space(NAcd *acd) {
284284

285285
max_map = 2 * acd->max_bpf_map;
286286

287+
/* If we didn't succeed in creating a map during n_acd_new(),
288+
* let's assume we are unable to create it here, and skip it. */
289+
if (!n_acd_has_bpf(acd)) {
290+
goto out;
291+
}
292+
293+
/* If we are unable to acquire a BPF map due to lacking permissions,
294+
* let's fail silently here and possibly try again on next call. */
287295
r = n_acd_bpf_map_create(&fd_map, max_map);
288-
if (r)
296+
if (r) {
297+
if (r == -EPERM) {
298+
goto out;
299+
}
300+
289301
return r;
302+
}
290303

291304
c_rbtree_for_each_entry(probe, &acd->ip_tree, ip_node) {
292305
r = n_acd_bpf_map_add(fd_map, &probe->ip);
@@ -308,6 +321,8 @@ int n_acd_ensure_bpf_map_space(NAcd *acd) {
308321
close(acd->fd_bpf_map);
309322
acd->fd_bpf_map = fd_map;
310323
fd_map = -1;
324+
325+
out:
311326
acd->max_bpf_map = max_map;
312327
return 0;
313328
}
@@ -360,12 +375,13 @@ _c_public_ int n_acd_new(NAcd **acdp, NAcdConfig *config) {
360375
acd->max_bpf_map = 8;
361376

362377
r = n_acd_bpf_map_create(&acd->fd_bpf_map, acd->max_bpf_map);
363-
if (r)
364-
return r;
365-
366-
r = n_acd_bpf_compile(&fd_bpf_prog, acd->fd_bpf_map, (struct ether_addr*) acd->mac);
367-
if (r)
368-
return r;
378+
if (!r) {
379+
r = n_acd_bpf_compile(&fd_bpf_prog, acd->fd_bpf_map, (struct ether_addr*) acd->mac);
380+
if (r) {
381+
close(acd->fd_bpf_map);
382+
acd->fd_bpf_map = -1;
383+
}
384+
}
369385

370386
r = n_acd_socket_new(&acd->fd_socket, fd_bpf_prog, config);
371387
if (r)
@@ -572,6 +588,20 @@ _c_public_ void n_acd_get_fd(NAcd *acd, int *fdp) {
572588
*fdp = acd->fd_epoll;
573589
}
574590

591+
/**
592+
* n_acd_has_bpf() - query the usage of eBPF
593+
* @acd: context object to operate on
594+
*
595+
* Checks whether the ACD probe is using eBPF or not.
596+
*
597+
* Return: true if the probe is using eBPF, or
598+
* false if the probe failed to configure eBPF
599+
* (e.g. due to missing capabilities)
600+
*/
601+
_c_public_ bool n_acd_has_bpf(NAcd *acd) {
602+
return acd->fd_bpf_map != -1;
603+
}
604+
575605
static int n_acd_handle_timeout(NAcd *acd) {
576606
NAcdProbe *probe;
577607
uint64_t now;

src/n-acd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ NAcd *n_acd_ref(NAcd *acd);
9393
NAcd *n_acd_unref(NAcd *acd);
9494

9595
void n_acd_get_fd(NAcd *acd, int *fdp);
96+
bool n_acd_has_bpf(NAcd *acd);
9697
int n_acd_dispatch(NAcd *acd);
9798
int n_acd_pop_event(NAcd *acd, NAcdEvent **eventp);
9899

src/test-api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ static void test_api_functions(void) {
5656
(void *)n_acd_ref,
5757
(void *)n_acd_unref,
5858
(void *)n_acd_get_fd,
59+
(void *)n_acd_has_bpf,
5960
(void *)n_acd_dispatch,
6061
(void *)n_acd_pop_event,
6162
(void *)n_acd_probe,

src/test-bpf.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include "n-acd-private.h"
2020
#include "test.h"
2121

22+
// https://mesonbuild.com/Unit-tests.html#skipped-tests-and-hard-errors
23+
#define RETURN_TEST_SKIPPED 77
24+
2225
#define ETHER_ARP_PACKET_INIT(_op, _mac, _sip, _tip) { \
2326
.ea_hdr = { \
2427
.ar_hrd = htobe16(ARPHRD_ETHER), \
@@ -43,11 +46,15 @@
4346
.arp_tpa[3] = be32toh((_tip)->s_addr) & 0xff, \
4447
}
4548

46-
static void test_map(void) {
49+
static int test_map(void) {
4750
int r, mapfd = -1;
4851
struct in_addr addr = { 1 };
4952

5053
r = n_acd_bpf_map_create(&mapfd, 8);
54+
if (r == -EPERM) {
55+
return RETURN_TEST_SKIPPED;
56+
}
57+
5158
c_assert(r >= 0);
5259
c_assert(mapfd >= 0);
5360

@@ -67,6 +74,7 @@ static void test_map(void) {
6774
c_assert(r == -ENOENT);
6875

6976
close(mapfd);
77+
return 0;
7078
}
7179

7280
static void verify_success(struct ether_arp *packet, int out_fd, int in_fd) {
@@ -92,7 +100,7 @@ static void verify_failure(struct ether_arp *packet, int out_fd, int in_fd) {
92100
c_assert(errno == EAGAIN);
93101
}
94102

95-
static void test_filter(void) {
103+
static int test_filter(void) {
96104
uint8_t buf[sizeof(struct ether_arp) + 1] = {};
97105
struct ether_addr mac1 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 } };
98106
struct ether_addr mac2 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x07 } };
@@ -103,6 +111,10 @@ static void test_filter(void) {
103111
int r, mapfd = -1, progfd = -1, pair[2];
104112

105113
r = n_acd_bpf_map_create(&mapfd, 1);
114+
if (r == -EPERM) {
115+
return RETURN_TEST_SKIPPED;
116+
}
117+
106118
c_assert(r >= 0);
107119

108120
r = n_acd_bpf_compile(&progfd, mapfd, &mac1);
@@ -214,13 +226,21 @@ static void test_filter(void) {
214226
close(pair[1]);
215227
close(progfd);
216228
close(mapfd);
229+
230+
return 0;
217231
}
218232

219233
int main(int argc, char **argv) {
234+
int r;
220235
test_setup();
221236

222-
test_map();
223-
test_filter();
237+
if ((r = test_map())) {
238+
return r;
239+
}
240+
241+
if ((r = test_filter())) {
242+
return r;
243+
}
224244

225245
return 0;
226246
}

0 commit comments

Comments
 (0)