ipv6_flowlabel_mgr.c (5506B)
1// SPDX-License-Identifier: GPL-2.0 2/* Test IPV6_FLOWINFO_MGR */ 3 4#define _GNU_SOURCE 5 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_MGR 25#define IPV6_FLOWLABEL_MGR 32 26#endif 27 28/* from net/ipv6/ip6_flowlabel.c */ 29#define FL_MIN_LINGER 6 30 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 else 139 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}