17
17
#include <stdlib.h>
18
18
19
19
#include <sys/wait.h>
20
-
20
+ #include <sys/socket.h>
21
+ #include <netinet/in.h>
21
22
#include <linux/bpf.h>
22
23
23
24
#include <bpf/bpf.h>
26
27
#include "bpf_util.h"
27
28
#include "bpf_rlimit.h"
28
29
30
+ #ifndef ENOTSUPP
31
+ #define ENOTSUPP 524
32
+ #endif
33
+
29
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
45
static void test_hashmap (int task , void * data )
32
46
{
33
47
long long key , next_key , first_key , value ;
@@ -1150,6 +1164,250 @@ static void test_map_wronly(void)
1150
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
1411
static void run_all_tests (void )
1154
1412
{
1155
1413
test_hashmap (0 , NULL );
@@ -1170,6 +1428,8 @@ static void run_all_tests(void)
1170
1428
1171
1429
test_map_rdonly ();
1172
1430
test_map_wronly ();
1431
+
1432
+ test_reuseport_array ();
1173
1433
}
1174
1434
1175
1435
int main (void )
0 commit comments