1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #define _GNU_SOURCE |
4 | |
5 | #include <errno.h> |
6 | #include <fcntl.h> |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <unistd.h> |
11 | #include <linux/if.h> |
12 | #include <linux/if_tun.h> |
13 | #include <linux/netlink.h> |
14 | #include <linux/rtnetlink.h> |
15 | #include <sys/ioctl.h> |
16 | #include <sys/socket.h> |
17 | |
18 | #include "../kselftest_harness.h" |
19 | |
20 | static int tun_attach(int fd, char *dev) |
21 | { |
22 | struct ifreq ifr; |
23 | |
24 | memset(&ifr, 0, sizeof(ifr)); |
25 | strcpy(p: ifr.ifr_name, q: dev); |
26 | ifr.ifr_flags = IFF_ATTACH_QUEUE; |
27 | |
28 | return ioctl(fd, TUNSETQUEUE, (void *) &ifr); |
29 | } |
30 | |
31 | static int tun_detach(int fd, char *dev) |
32 | { |
33 | struct ifreq ifr; |
34 | |
35 | memset(&ifr, 0, sizeof(ifr)); |
36 | strcpy(p: ifr.ifr_name, q: dev); |
37 | ifr.ifr_flags = IFF_DETACH_QUEUE; |
38 | |
39 | return ioctl(fd, TUNSETQUEUE, (void *) &ifr); |
40 | } |
41 | |
42 | static int tun_alloc(char *dev) |
43 | { |
44 | struct ifreq ifr; |
45 | int fd, err; |
46 | |
47 | fd = open("/dev/net/tun" , O_RDWR); |
48 | if (fd < 0) { |
49 | fprintf(stderr, "can't open tun: %s\n" , strerror(errno)); |
50 | return fd; |
51 | } |
52 | |
53 | memset(&ifr, 0, sizeof(ifr)); |
54 | strcpy(p: ifr.ifr_name, q: dev); |
55 | ifr.ifr_flags = IFF_TAP | IFF_NAPI | IFF_MULTI_QUEUE; |
56 | |
57 | err = ioctl(fd, TUNSETIFF, (void *) &ifr); |
58 | if (err < 0) { |
59 | fprintf(stderr, "can't TUNSETIFF: %s\n" , strerror(errno)); |
60 | close(fd); |
61 | return err; |
62 | } |
63 | strcpy(p: dev, q: ifr.ifr_name); |
64 | return fd; |
65 | } |
66 | |
67 | static int tun_delete(char *dev) |
68 | { |
69 | struct { |
70 | struct nlmsghdr nh; |
71 | struct ifinfomsg ifm; |
72 | unsigned char data[64]; |
73 | } req; |
74 | struct rtattr *rta; |
75 | int ret, rtnl; |
76 | |
77 | rtnl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); |
78 | if (rtnl < 0) { |
79 | fprintf(stderr, "can't open rtnl: %s\n" , strerror(errno)); |
80 | return 1; |
81 | } |
82 | |
83 | memset(&req, 0, sizeof(req)); |
84 | req.nh.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(req.ifm))); |
85 | req.nh.nlmsg_flags = NLM_F_REQUEST; |
86 | req.nh.nlmsg_type = RTM_DELLINK; |
87 | |
88 | req.ifm.ifi_family = AF_UNSPEC; |
89 | |
90 | rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len)); |
91 | rta->rta_type = IFLA_IFNAME; |
92 | rta->rta_len = RTA_LENGTH(IFNAMSIZ); |
93 | req.nh.nlmsg_len += rta->rta_len; |
94 | memcpy(RTA_DATA(rta), dev, IFNAMSIZ); |
95 | |
96 | ret = send(rtnl, &req, req.nh.nlmsg_len, 0); |
97 | if (ret < 0) |
98 | fprintf(stderr, "can't send: %s\n" , strerror(errno)); |
99 | ret = (unsigned int)ret != req.nh.nlmsg_len; |
100 | |
101 | close(rtnl); |
102 | return ret; |
103 | } |
104 | |
105 | FIXTURE(tun) |
106 | { |
107 | char ifname[IFNAMSIZ]; |
108 | int fd, fd2; |
109 | }; |
110 | |
111 | FIXTURE_SETUP(tun) |
112 | { |
113 | memset(self->ifname, 0, sizeof(self->ifname)); |
114 | |
115 | self->fd = tun_alloc(dev: self->ifname); |
116 | ASSERT_GE(self->fd, 0); |
117 | |
118 | self->fd2 = tun_alloc(dev: self->ifname); |
119 | ASSERT_GE(self->fd2, 0); |
120 | } |
121 | |
122 | FIXTURE_TEARDOWN(tun) |
123 | { |
124 | if (self->fd >= 0) |
125 | close(self->fd); |
126 | if (self->fd2 >= 0) |
127 | close(self->fd2); |
128 | } |
129 | |
130 | TEST_F(tun, delete_detach_close) { |
131 | EXPECT_EQ(tun_delete(self->ifname), 0); |
132 | EXPECT_EQ(tun_detach(self->fd, self->ifname), -1); |
133 | EXPECT_EQ(errno, 22); |
134 | } |
135 | |
136 | TEST_F(tun, detach_delete_close) { |
137 | EXPECT_EQ(tun_detach(self->fd, self->ifname), 0); |
138 | EXPECT_EQ(tun_delete(self->ifname), 0); |
139 | } |
140 | |
141 | TEST_F(tun, detach_close_delete) { |
142 | EXPECT_EQ(tun_detach(self->fd, self->ifname), 0); |
143 | close(self->fd); |
144 | self->fd = -1; |
145 | EXPECT_EQ(tun_delete(self->ifname), 0); |
146 | } |
147 | |
148 | TEST_F(tun, reattach_delete_close) { |
149 | EXPECT_EQ(tun_detach(self->fd, self->ifname), 0); |
150 | EXPECT_EQ(tun_attach(self->fd, self->ifname), 0); |
151 | EXPECT_EQ(tun_delete(self->ifname), 0); |
152 | } |
153 | |
154 | TEST_F(tun, reattach_close_delete) { |
155 | EXPECT_EQ(tun_detach(self->fd, self->ifname), 0); |
156 | EXPECT_EQ(tun_attach(self->fd, self->ifname), 0); |
157 | close(self->fd); |
158 | self->fd = -1; |
159 | EXPECT_EQ(tun_delete(self->ifname), 0); |
160 | } |
161 | |
162 | TEST_HARNESS_MAIN |
163 | |