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