xref: /openbmc/linux/tools/testing/selftests/net/tun.c (revision dff03381)
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(ifr.ifr_name, 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(ifr.ifr_name, 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(ifr.ifr_name, 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(dev, 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(self->ifname);
116 	ASSERT_GE(self->fd, 0);
117 
118 	self->fd2 = tun_alloc(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