diff options
author | Martin KaFai Lau <kafai@fb.com> | 2018-08-08 04:01:30 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-08-10 19:58:46 -0400 |
commit | 6bc8529c414f931ce0acef3099b015cf2f5c4291 (patch) | |
tree | 303a784ee3ef7c306d57d57ca1f845379bf6cf41 /tools | |
parent | 3bd43a8c91cba0493e507ed7baefa9b5613c28a9 (diff) |
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 <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/lib/bpf/libbpf.c | 1 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_maps.c | 262 |
2 files changed, 262 insertions, 1 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 40211b51427a..2abd0f112627 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -1501,6 +1501,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type) | |||
1501 | case BPF_PROG_TYPE_SK_MSG: | 1501 | case BPF_PROG_TYPE_SK_MSG: |
1502 | case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: | 1502 | case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: |
1503 | case BPF_PROG_TYPE_LIRC_MODE2: | 1503 | case BPF_PROG_TYPE_LIRC_MODE2: |
1504 | case BPF_PROG_TYPE_SK_REUSEPORT: | ||
1504 | return false; | 1505 | return false; |
1505 | case BPF_PROG_TYPE_UNSPEC: | 1506 | case BPF_PROG_TYPE_UNSPEC: |
1506 | case BPF_PROG_TYPE_KPROBE: | 1507 | case BPF_PROG_TYPE_KPROBE: |
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 6c253343a6f9..4b7c74f5faa7 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c | |||
@@ -17,7 +17,8 @@ | |||
17 | #include <stdlib.h> | 17 | #include <stdlib.h> |
18 | 18 | ||
19 | #include <sys/wait.h> | 19 | #include <sys/wait.h> |
20 | 20 | #include <sys/socket.h> | |
21 | #include <netinet/in.h> | ||
21 | #include <linux/bpf.h> | 22 | #include <linux/bpf.h> |
22 | 23 | ||
23 | #include <bpf/bpf.h> | 24 | #include <bpf/bpf.h> |
@@ -26,8 +27,21 @@ | |||
26 | #include "bpf_util.h" | 27 | #include "bpf_util.h" |
27 | #include "bpf_rlimit.h" | 28 | #include "bpf_rlimit.h" |
28 | 29 | ||
30 | #ifndef ENOTSUPP | ||
31 | #define ENOTSUPP 524 | ||
32 | #endif | ||
33 | |||
29 | static int map_flags; | 34 | static int map_flags; |
30 | 35 | ||
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 | |||
31 | static void test_hashmap(int task, void *data) | 45 | static void test_hashmap(int task, void *data) |
32 | { | 46 | { |
33 | long long key, next_key, first_key, value; | 47 | long long key, next_key, first_key, value; |
@@ -1150,6 +1164,250 @@ static void test_map_wronly(void) | |||
1150 | assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM); | 1164 | assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM); |
1151 | } | 1165 | } |
1152 | 1166 | ||
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 | |||
1153 | static void run_all_tests(void) | 1411 | static void run_all_tests(void) |
1154 | { | 1412 | { |
1155 | test_hashmap(0, NULL); | 1413 | test_hashmap(0, NULL); |
@@ -1170,6 +1428,8 @@ static void run_all_tests(void) | |||
1170 | 1428 | ||
1171 | test_map_rdonly(); | 1429 | test_map_rdonly(); |
1172 | test_map_wronly(); | 1430 | test_map_wronly(); |
1431 | |||
1432 | test_reuseport_array(); | ||
1173 | } | 1433 | } |
1174 | 1434 | ||
1175 | int main(void) | 1435 | int main(void) |