Skip to content

Commit 6bc8529

Browse files
iamkafaiborkmann
authored andcommitted
bpf: test BPF_MAP_TYPE_REUSEPORT_SOCKARRAY
This patch adds tests for the new BPF_MAP_TYPE_REUSEPORT_SOCKARRAY. Signed-off-by: Martin KaFai Lau <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 3bd43a8 commit 6bc8529

File tree

2 files changed

+262
-1
lines changed

2 files changed

+262
-1
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
15011501
case BPF_PROG_TYPE_SK_MSG:
15021502
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
15031503
case BPF_PROG_TYPE_LIRC_MODE2:
1504+
case BPF_PROG_TYPE_SK_REUSEPORT:
15041505
return false;
15051506
case BPF_PROG_TYPE_UNSPEC:
15061507
case BPF_PROG_TYPE_KPROBE:

tools/testing/selftests/bpf/test_maps.c

Lines changed: 261 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
#include <stdlib.h>
1818

1919
#include <sys/wait.h>
20-
20+
#include <sys/socket.h>
21+
#include <netinet/in.h>
2122
#include <linux/bpf.h>
2223

2324
#include <bpf/bpf.h>
@@ -26,8 +27,21 @@
2627
#include "bpf_util.h"
2728
#include "bpf_rlimit.h"
2829

30+
#ifndef ENOTSUPP
31+
#define ENOTSUPP 524
32+
#endif
33+
2934
static int map_flags;
3035

36+
#define CHECK(condition, tag, format...) ({ \
37+
int __ret = !!(condition); \
38+
if (__ret) { \
39+
printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \
40+
printf(format); \
41+
exit(-1); \
42+
} \
43+
})
44+
3145
static void test_hashmap(int task, void *data)
3246
{
3347
long long key, next_key, first_key, value;
@@ -1150,6 +1164,250 @@ static void test_map_wronly(void)
11501164
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
11511165
}
11521166

1167+
static void prepare_reuseport_grp(int type, int map_fd,
1168+
__s64 *fds64, __u64 *sk_cookies,
1169+
unsigned int n)
1170+
{
1171+
socklen_t optlen, addrlen;
1172+
struct sockaddr_in6 s6;
1173+
const __u32 index0 = 0;
1174+
const int optval = 1;
1175+
unsigned int i;
1176+
u64 sk_cookie;
1177+
__s64 fd64;
1178+
int err;
1179+
1180+
s6.sin6_family = AF_INET6;
1181+
s6.sin6_addr = in6addr_any;
1182+
s6.sin6_port = 0;
1183+
addrlen = sizeof(s6);
1184+
optlen = sizeof(sk_cookie);
1185+
1186+
for (i = 0; i < n; i++) {
1187+
fd64 = socket(AF_INET6, type, 0);
1188+
CHECK(fd64 == -1, "socket()",
1189+
"sock_type:%d fd64:%lld errno:%d\n",
1190+
type, fd64, errno);
1191+
1192+
err = setsockopt(fd64, SOL_SOCKET, SO_REUSEPORT,
1193+
&optval, sizeof(optval));
1194+
CHECK(err == -1, "setsockopt(SO_REUSEEPORT)",
1195+
"err:%d errno:%d\n", err, errno);
1196+
1197+
/* reuseport_array does not allow unbound sk */
1198+
err = bpf_map_update_elem(map_fd, &index0, &fd64,
1199+
BPF_ANY);
1200+
CHECK(err != -1 || errno != EINVAL,
1201+
"reuseport array update unbound sk",
1202+
"sock_type:%d err:%d errno:%d\n",
1203+
type, err, errno);
1204+
1205+
err = bind(fd64, (struct sockaddr *)&s6, sizeof(s6));
1206+
CHECK(err == -1, "bind()",
1207+
"sock_type:%d err:%d errno:%d\n", type, err, errno);
1208+
1209+
if (i == 0) {
1210+
err = getsockname(fd64, (struct sockaddr *)&s6,
1211+
&addrlen);
1212+
CHECK(err == -1, "getsockname()",
1213+
"sock_type:%d err:%d errno:%d\n",
1214+
type, err, errno);
1215+
}
1216+
1217+
err = getsockopt(fd64, SOL_SOCKET, SO_COOKIE, &sk_cookie,
1218+
&optlen);
1219+
CHECK(err == -1, "getsockopt(SO_COOKIE)",
1220+
"sock_type:%d err:%d errno:%d\n", type, err, errno);
1221+
1222+
if (type == SOCK_STREAM) {
1223+
/*
1224+
* reuseport_array does not allow
1225+
* non-listening tcp sk.
1226+
*/
1227+
err = bpf_map_update_elem(map_fd, &index0, &fd64,
1228+
BPF_ANY);
1229+
CHECK(err != -1 || errno != EINVAL,
1230+
"reuseport array update non-listening sk",
1231+
"sock_type:%d err:%d errno:%d\n",
1232+
type, err, errno);
1233+
err = listen(fd64, 0);
1234+
CHECK(err == -1, "listen()",
1235+
"sock_type:%d, err:%d errno:%d\n",
1236+
type, err, errno);
1237+
}
1238+
1239+
fds64[i] = fd64;
1240+
sk_cookies[i] = sk_cookie;
1241+
}
1242+
}
1243+
1244+
static void test_reuseport_array(void)
1245+
{
1246+
#define REUSEPORT_FD_IDX(err, last) ({ (err) ? last : !last; })
1247+
1248+
const __u32 array_size = 4, index0 = 0, index3 = 3;
1249+
int types[2] = { SOCK_STREAM, SOCK_DGRAM }, type;
1250+
__u64 grpa_cookies[2], sk_cookie, map_cookie;
1251+
__s64 grpa_fds64[2] = { -1, -1 }, fd64 = -1;
1252+
const __u32 bad_index = array_size;
1253+
int map_fd, err, t, f;
1254+
__u32 fds_idx = 0;
1255+
int fd;
1256+
1257+
map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1258+
sizeof(__u32), sizeof(__u64), array_size, 0);
1259+
CHECK(map_fd == -1, "reuseport array create",
1260+
"map_fd:%d, errno:%d\n", map_fd, errno);
1261+
1262+
/* Test lookup/update/delete with invalid index */
1263+
err = bpf_map_delete_elem(map_fd, &bad_index);
1264+
CHECK(err != -1 || errno != E2BIG, "reuseport array del >=max_entries",
1265+
"err:%d errno:%d\n", err, errno);
1266+
1267+
err = bpf_map_update_elem(map_fd, &bad_index, &fd64, BPF_ANY);
1268+
CHECK(err != -1 || errno != E2BIG,
1269+
"reuseport array update >=max_entries",
1270+
"err:%d errno:%d\n", err, errno);
1271+
1272+
err = bpf_map_lookup_elem(map_fd, &bad_index, &map_cookie);
1273+
CHECK(err != -1 || errno != ENOENT,
1274+
"reuseport array update >=max_entries",
1275+
"err:%d errno:%d\n", err, errno);
1276+
1277+
/* Test lookup/delete non existence elem */
1278+
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
1279+
CHECK(err != -1 || errno != ENOENT,
1280+
"reuseport array lookup not-exist elem",
1281+
"err:%d errno:%d\n", err, errno);
1282+
err = bpf_map_delete_elem(map_fd, &index3);
1283+
CHECK(err != -1 || errno != ENOENT,
1284+
"reuseport array del not-exist elem",
1285+
"err:%d errno:%d\n", err, errno);
1286+
1287+
for (t = 0; t < ARRAY_SIZE(types); t++) {
1288+
type = types[t];
1289+
1290+
prepare_reuseport_grp(type, map_fd, grpa_fds64,
1291+
grpa_cookies, ARRAY_SIZE(grpa_fds64));
1292+
1293+
/* Test BPF_* update flags */
1294+
/* BPF_EXIST failure case */
1295+
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
1296+
BPF_EXIST);
1297+
CHECK(err != -1 || errno != ENOENT,
1298+
"reuseport array update empty elem BPF_EXIST",
1299+
"sock_type:%d err:%d errno:%d\n",
1300+
type, err, errno);
1301+
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
1302+
1303+
/* BPF_NOEXIST success case */
1304+
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
1305+
BPF_NOEXIST);
1306+
CHECK(err == -1,
1307+
"reuseport array update empty elem BPF_NOEXIST",
1308+
"sock_type:%d err:%d errno:%d\n",
1309+
type, err, errno);
1310+
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
1311+
1312+
/* BPF_EXIST success case. */
1313+
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
1314+
BPF_EXIST);
1315+
CHECK(err == -1,
1316+
"reuseport array update same elem BPF_EXIST",
1317+
"sock_type:%d err:%d errno:%d\n", type, err, errno);
1318+
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
1319+
1320+
/* BPF_NOEXIST failure case */
1321+
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
1322+
BPF_NOEXIST);
1323+
CHECK(err != -1 || errno != EEXIST,
1324+
"reuseport array update non-empty elem BPF_NOEXIST",
1325+
"sock_type:%d err:%d errno:%d\n",
1326+
type, err, errno);
1327+
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
1328+
1329+
/* BPF_ANY case (always succeed) */
1330+
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
1331+
BPF_ANY);
1332+
CHECK(err == -1,
1333+
"reuseport array update same sk with BPF_ANY",
1334+
"sock_type:%d err:%d errno:%d\n", type, err, errno);
1335+
1336+
fd64 = grpa_fds64[fds_idx];
1337+
sk_cookie = grpa_cookies[fds_idx];
1338+
1339+
/* The same sk cannot be added to reuseport_array twice */
1340+
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_ANY);
1341+
CHECK(err != -1 || errno != EBUSY,
1342+
"reuseport array update same sk with same index",
1343+
"sock_type:%d err:%d errno:%d\n",
1344+
type, err, errno);
1345+
1346+
err = bpf_map_update_elem(map_fd, &index0, &fd64, BPF_ANY);
1347+
CHECK(err != -1 || errno != EBUSY,
1348+
"reuseport array update same sk with different index",
1349+
"sock_type:%d err:%d errno:%d\n",
1350+
type, err, errno);
1351+
1352+
/* Test delete elem */
1353+
err = bpf_map_delete_elem(map_fd, &index3);
1354+
CHECK(err == -1, "reuseport array delete sk",
1355+
"sock_type:%d err:%d errno:%d\n",
1356+
type, err, errno);
1357+
1358+
/* Add it back with BPF_NOEXIST */
1359+
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
1360+
CHECK(err == -1,
1361+
"reuseport array re-add with BPF_NOEXIST after del",
1362+
"sock_type:%d err:%d errno:%d\n", type, err, errno);
1363+
1364+
/* Test cookie */
1365+
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
1366+
CHECK(err == -1 || sk_cookie != map_cookie,
1367+
"reuseport array lookup re-added sk",
1368+
"sock_type:%d err:%d errno:%d sk_cookie:0x%llx map_cookie:0x%llxn",
1369+
type, err, errno, sk_cookie, map_cookie);
1370+
1371+
/* Test elem removed by close() */
1372+
for (f = 0; f < ARRAY_SIZE(grpa_fds64); f++)
1373+
close(grpa_fds64[f]);
1374+
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
1375+
CHECK(err != -1 || errno != ENOENT,
1376+
"reuseport array lookup after close()",
1377+
"sock_type:%d err:%d errno:%d\n",
1378+
type, err, errno);
1379+
}
1380+
1381+
/* Test SOCK_RAW */
1382+
fd64 = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
1383+
CHECK(fd64 == -1, "socket(SOCK_RAW)", "err:%d errno:%d\n",
1384+
err, errno);
1385+
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
1386+
CHECK(err != -1 || errno != ENOTSUPP, "reuseport array update SOCK_RAW",
1387+
"err:%d errno:%d\n", err, errno);
1388+
close(fd64);
1389+
1390+
/* Close the 64 bit value map */
1391+
close(map_fd);
1392+
1393+
/* Test 32 bit fd */
1394+
map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1395+
sizeof(__u32), sizeof(__u32), array_size, 0);
1396+
CHECK(map_fd == -1, "reuseport array create",
1397+
"map_fd:%d, errno:%d\n", map_fd, errno);
1398+
prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1);
1399+
fd = fd64;
1400+
err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
1401+
CHECK(err == -1, "reuseport array update 32 bit fd",
1402+
"err:%d errno:%d\n", err, errno);
1403+
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
1404+
CHECK(err != -1 || errno != ENOSPC,
1405+
"reuseport array lookup 32 bit fd",
1406+
"err:%d errno:%d\n", err, errno);
1407+
close(fd);
1408+
close(map_fd);
1409+
}
1410+
11531411
static void run_all_tests(void)
11541412
{
11551413
test_hashmap(0, NULL);
@@ -1170,6 +1428,8 @@ static void run_all_tests(void)
11701428

11711429
test_map_rdonly();
11721430
test_map_wronly();
1431+
1432+
test_reuseport_array();
11731433
}
11741434

11751435
int main(void)

0 commit comments

Comments
 (0)