linux/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
<<
>>
Prefs
   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}
 200