18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Test IPV6_FLOWINFO_MGR */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#define _GNU_SOURCE 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <arpa/inet.h> 78c2ecf20Sopenharmony_ci#include <error.h> 88c2ecf20Sopenharmony_ci#include <errno.h> 98c2ecf20Sopenharmony_ci#include <limits.h> 108c2ecf20Sopenharmony_ci#include <linux/in6.h> 118c2ecf20Sopenharmony_ci#include <stdbool.h> 128c2ecf20Sopenharmony_ci#include <stdio.h> 138c2ecf20Sopenharmony_ci#include <stdint.h> 148c2ecf20Sopenharmony_ci#include <stdlib.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci#include <sys/socket.h> 178c2ecf20Sopenharmony_ci#include <sys/stat.h> 188c2ecf20Sopenharmony_ci#include <sys/time.h> 198c2ecf20Sopenharmony_ci#include <sys/types.h> 208c2ecf20Sopenharmony_ci#include <sys/wait.h> 218c2ecf20Sopenharmony_ci#include <unistd.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* uapi/glibc weirdness may leave this undefined */ 248c2ecf20Sopenharmony_ci#ifndef IPV6_FLOWLABEL_MGR 258c2ecf20Sopenharmony_ci#define IPV6_FLOWLABEL_MGR 32 268c2ecf20Sopenharmony_ci#endif 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* from net/ipv6/ip6_flowlabel.c */ 298c2ecf20Sopenharmony_ci#define FL_MIN_LINGER 6 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define explain(x) \ 328c2ecf20Sopenharmony_ci do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define __expect(x) \ 358c2ecf20Sopenharmony_ci do { \ 368c2ecf20Sopenharmony_ci if (!(x)) \ 378c2ecf20Sopenharmony_ci fprintf(stderr, "[OK] " #x "\n"); \ 388c2ecf20Sopenharmony_ci else \ 398c2ecf20Sopenharmony_ci error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \ 408c2ecf20Sopenharmony_ci } while (0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define expect_pass(x) __expect(x) 438c2ecf20Sopenharmony_ci#define expect_fail(x) __expect(!(x)) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic bool cfg_long_running; 468c2ecf20Sopenharmony_cistatic bool cfg_verbose; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct in6_flowlabel_req req = { 518c2ecf20Sopenharmony_ci .flr_action = IPV6_FL_A_GET, 528c2ecf20Sopenharmony_ci .flr_label = htonl(label), 538c2ecf20Sopenharmony_ci .flr_flags = flags, 548c2ecf20Sopenharmony_ci .flr_share = share, 558c2ecf20Sopenharmony_ci }; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */ 588c2ecf20Sopenharmony_ci req.flr_dst.s6_addr[0] = 0xfd; 598c2ecf20Sopenharmony_ci req.flr_dst.s6_addr[15] = 0x1; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int flowlabel_put(int fd, uint32_t label) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct in6_flowlabel_req req = { 678c2ecf20Sopenharmony_ci .flr_action = IPV6_FL_A_PUT, 688c2ecf20Sopenharmony_ci .flr_label = htonl(label), 698c2ecf20Sopenharmony_ci }; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void run_tests(int fd) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int wstatus; 778c2ecf20Sopenharmony_ci pid_t pid; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci explain("cannot get non-existent label"); 808c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci explain("cannot put non-existent label"); 838c2ecf20Sopenharmony_ci expect_fail(flowlabel_put(fd, 1)); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci explain("cannot create label greater than 20 bits"); 868c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY, 878c2ecf20Sopenharmony_ci IPV6_FL_F_CREATE)); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci explain("create a new label (FL_F_CREATE)"); 908c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); 918c2ecf20Sopenharmony_ci explain("can get the label (without FL_F_CREATE)"); 928c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); 938c2ecf20Sopenharmony_ci explain("can get it again with create flag set, too"); 948c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); 958c2ecf20Sopenharmony_ci explain("cannot get it again with the exclusive (FL_FL_EXCL) flag"); 968c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 978c2ecf20Sopenharmony_ci IPV6_FL_F_CREATE | IPV6_FL_F_EXCL)); 988c2ecf20Sopenharmony_ci explain("can now put exactly three references"); 998c2ecf20Sopenharmony_ci expect_pass(flowlabel_put(fd, 1)); 1008c2ecf20Sopenharmony_ci expect_pass(flowlabel_put(fd, 1)); 1018c2ecf20Sopenharmony_ci expect_pass(flowlabel_put(fd, 1)); 1028c2ecf20Sopenharmony_ci expect_fail(flowlabel_put(fd, 1)); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci explain("create a new exclusive label (FL_S_EXCL)"); 1058c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); 1068c2ecf20Sopenharmony_ci explain("cannot get it again in non-exclusive mode"); 1078c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); 1088c2ecf20Sopenharmony_ci explain("cannot get it again in exclusive mode either"); 1098c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); 1108c2ecf20Sopenharmony_ci expect_pass(flowlabel_put(fd, 2)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (cfg_long_running) { 1138c2ecf20Sopenharmony_ci explain("cannot reuse the label, due to linger"); 1148c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, 1158c2ecf20Sopenharmony_ci IPV6_FL_F_CREATE)); 1168c2ecf20Sopenharmony_ci explain("after sleep, can reuse"); 1178c2ecf20Sopenharmony_ci sleep(FL_MIN_LINGER * 2 + 1); 1188c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY, 1198c2ecf20Sopenharmony_ci IPV6_FL_F_CREATE)); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci explain("create a new user-private label (FL_S_USER)"); 1238c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE)); 1248c2ecf20Sopenharmony_ci explain("cannot get it again in non-exclusive mode"); 1258c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0)); 1268c2ecf20Sopenharmony_ci explain("cannot get it again in exclusive mode"); 1278c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0)); 1288c2ecf20Sopenharmony_ci explain("can get it again in user mode"); 1298c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); 1308c2ecf20Sopenharmony_ci explain("child process can get it too, but not after setuid(nobody)"); 1318c2ecf20Sopenharmony_ci pid = fork(); 1328c2ecf20Sopenharmony_ci if (pid == -1) 1338c2ecf20Sopenharmony_ci error(1, errno, "fork"); 1348c2ecf20Sopenharmony_ci if (!pid) { 1358c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); 1368c2ecf20Sopenharmony_ci if (setuid(USHRT_MAX)) 1378c2ecf20Sopenharmony_ci fprintf(stderr, "[INFO] skip setuid child test\n"); 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); 1408c2ecf20Sopenharmony_ci exit(0); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci if (wait(&wstatus) == -1) 1438c2ecf20Sopenharmony_ci error(1, errno, "wait"); 1448c2ecf20Sopenharmony_ci if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 1458c2ecf20Sopenharmony_ci error(1, errno, "wait: unexpected child result"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci explain("create a new process-private label (FL_S_PROCESS)"); 1488c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE)); 1498c2ecf20Sopenharmony_ci explain("can get it again"); 1508c2ecf20Sopenharmony_ci expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); 1518c2ecf20Sopenharmony_ci explain("child process cannot can get it"); 1528c2ecf20Sopenharmony_ci pid = fork(); 1538c2ecf20Sopenharmony_ci if (pid == -1) 1548c2ecf20Sopenharmony_ci error(1, errno, "fork"); 1558c2ecf20Sopenharmony_ci if (!pid) { 1568c2ecf20Sopenharmony_ci expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); 1578c2ecf20Sopenharmony_ci exit(0); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci if (wait(&wstatus) == -1) 1608c2ecf20Sopenharmony_ci error(1, errno, "wait"); 1618c2ecf20Sopenharmony_ci if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) 1628c2ecf20Sopenharmony_ci error(1, errno, "wait: unexpected child result"); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void parse_opts(int argc, char **argv) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int c; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci while ((c = getopt(argc, argv, "lv")) != -1) { 1708c2ecf20Sopenharmony_ci switch (c) { 1718c2ecf20Sopenharmony_ci case 'l': 1728c2ecf20Sopenharmony_ci cfg_long_running = true; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case 'v': 1758c2ecf20Sopenharmony_ci cfg_verbose = true; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci default: 1788c2ecf20Sopenharmony_ci error(1, 0, "%s: parse error", argv[0]); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int fd; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci parse_opts(argc, argv); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci fd = socket(PF_INET6, SOCK_DGRAM, 0); 1908c2ecf20Sopenharmony_ci if (fd == -1) 1918c2ecf20Sopenharmony_ci error(1, errno, "socket"); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci run_tests(fd); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (close(fd)) 1968c2ecf20Sopenharmony_ci error(1, errno, "close"); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 200