200 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Test IPV6_FLOWINFO_MGR */3 4#define _GNU_SOURCE5 6#include <arpa/inet.h>7#include <error.h>8#include <errno.h>9#include <limits.h>10#include <linux/in6.h>11#include <stdbool.h>12#include <stdio.h>13#include <stdint.h>14#include <stdlib.h>15#include <string.h>16#include <sys/socket.h>17#include <sys/stat.h>18#include <sys/time.h>19#include <sys/types.h>20#include <sys/wait.h>21#include <unistd.h>22 23/* uapi/glibc weirdness may leave this undefined */24#ifndef IPV6_FLOWLABEL_MGR25#define IPV6_FLOWLABEL_MGR 3226#endif27 28/* from net/ipv6/ip6_flowlabel.c */29#define FL_MIN_LINGER 630 31#define explain(x) \32 do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0)33 34#define __expect(x) \35 do { \36 if (!(x)) \37 fprintf(stderr, "[OK] " #x "\n"); \38 else \39 error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \40 } while (0)41 42#define expect_pass(x) __expect(x)43#define expect_fail(x) __expect(!(x))44 45static bool cfg_long_running;46static bool cfg_verbose;47 48static int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags)49{50 struct in6_flowlabel_req req = {51 .flr_action = IPV6_FL_A_GET,52 .flr_label = htonl(label),53 .flr_flags = flags,54 .flr_share = share,55 };56 57 /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */58 req.flr_dst.s6_addr[0] = 0xfd;59 req.flr_dst.s6_addr[15] = 0x1;60 61 return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));62}63 64static int flowlabel_put(int fd, uint32_t label)65{66 struct in6_flowlabel_req req = {67 .flr_action = IPV6_FL_A_PUT,68 .flr_label = htonl(label),69 };70 71 return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));72}73 74static void run_tests(int fd)75{76 int wstatus;77 pid_t pid;78 79 explain("cannot get non-existent label");80 expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));81 82 explain("cannot put non-existent label");83 expect_fail(flowlabel_put(fd, 1));84 85 explain("cannot create label greater than 20 bits");86 expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY,87 IPV6_FL_F_CREATE));88 89 explain("create a new label (FL_F_CREATE)");90 expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));91 explain("can get the label (without FL_F_CREATE)");92 expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));93 explain("can get it again with create flag set, too");94 expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));95 explain("cannot get it again with the exclusive (FL_FL_EXCL) flag");96 expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY,97 IPV6_FL_F_CREATE | IPV6_FL_F_EXCL));98 explain("can now put exactly three references");99 expect_pass(flowlabel_put(fd, 1));100 expect_pass(flowlabel_put(fd, 1));101 expect_pass(flowlabel_put(fd, 1));102 expect_fail(flowlabel_put(fd, 1));103 104 explain("create a new exclusive label (FL_S_EXCL)");105 expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));106 explain("cannot get it again in non-exclusive mode");107 expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));108 explain("cannot get it again in exclusive mode either");109 expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));110 expect_pass(flowlabel_put(fd, 2));111 112 if (cfg_long_running) {113 explain("cannot reuse the label, due to linger");114 expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY,115 IPV6_FL_F_CREATE));116 explain("after sleep, can reuse");117 sleep(FL_MIN_LINGER * 2 + 1);118 expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY,119 IPV6_FL_F_CREATE));120 }121 122 explain("create a new user-private label (FL_S_USER)");123 expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE));124 explain("cannot get it again in non-exclusive mode");125 expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0));126 explain("cannot get it again in exclusive mode");127 expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0));128 explain("can get it again in user mode");129 expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));130 explain("child process can get it too, but not after setuid(nobody)");131 pid = fork();132 if (pid == -1)133 error(1, errno, "fork");134 if (!pid) {135 expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));136 if (setuid(USHRT_MAX))137 fprintf(stderr, "[INFO] skip setuid child test\n");138 else139 expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));140 exit(0);141 }142 if (wait(&wstatus) == -1)143 error(1, errno, "wait");144 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)145 error(1, errno, "wait: unexpected child result");146 147 explain("create a new process-private label (FL_S_PROCESS)");148 expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE));149 explain("can get it again");150 expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));151 explain("child process cannot can get it");152 pid = fork();153 if (pid == -1)154 error(1, errno, "fork");155 if (!pid) {156 expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));157 exit(0);158 }159 if (wait(&wstatus) == -1)160 error(1, errno, "wait");161 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)162 error(1, errno, "wait: unexpected child result");163}164 165static void parse_opts(int argc, char **argv)166{167 int c;168 169 while ((c = getopt(argc, argv, "lv")) != -1) {170 switch (c) {171 case 'l':172 cfg_long_running = true;173 break;174 case 'v':175 cfg_verbose = true;176 break;177 default:178 error(1, 0, "%s: parse error", argv[0]);179 }180 }181}182 183int main(int argc, char **argv)184{185 int fd;186 187 parse_opts(argc, argv);188 189 fd = socket(PF_INET6, SOCK_DGRAM, 0);190 if (fd == -1)191 error(1, errno, "socket");192 193 run_tests(fd);194 195 if (close(fd))196 error(1, errno, "close");197 198 return 0;199}200