1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"netdev N",	"start N workers exercising netdevice ioctls" },
29 	{ NULL,	"netdev-ops N",	"stop netdev workers after N bogo operations" },
30 	{ NULL,	NULL,		NULL }
31 };
32 
33 #if defined(__linux__) &&	\
34     defined(SIOCGIFCONF) &&	\
35     defined(HAVE_IFCONF)
36 
37 /*
38  *  As per man 7 netdevice advise, workaround glibc 2.1 missing
39  *  ifr_newname
40  */
41 #ifndef ifr_newname
42 #define ifr_newname     ifr_ifru.ifru_slave
43 #endif
44 
45 /*
46  *  stress_netdev_check()
47  *	helper to perform netdevice ioctl and check for failure
48  */
stress_netdev_check(const stress_args_t * args,struct ifreq * ifr,const int fd,const unsigned long cmd,const char * cmd_name)49 static void stress_netdev_check(
50 	const stress_args_t *args,
51 	struct ifreq *ifr,
52 	const int fd,
53 	const unsigned long cmd,
54 	const char *cmd_name)
55 {
56 	if (ioctl(fd, cmd, ifr) < 0) {
57 		if ((errno != ENOTTY) &&
58 		    (errno != EINVAL) &&
59 		    (errno != EADDRNOTAVAIL) &&
60 		    (errno != EOPNOTSUPP) &&
61 		    (errno != EBUSY) &&
62 		    (errno != EPERM))
63 			pr_fail("%s: interface '%s' ioctl %s failed, errno=%d (%s)\n",
64 				args->name, ifr->ifr_name, cmd_name,
65 				errno, strerror(errno));
66 	}
67 }
68 
69 #define STRESS_NETDEV_CHECK(args, ifr, fd, cmd)	\
70 	stress_netdev_check(args, ifr, fd, cmd, #cmd)
71 
72 /*
73  *  stress_netdev
74  *	stress netdev
75  */
stress_netdev(const stress_args_t * args)76 static int stress_netdev(const stress_args_t *args)
77 {
78 	int fd, rc = EXIT_SUCCESS;
79 
80 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
81 		pr_fail("%s: socket failed, errno=%d (%s)\n",
82 			args->name, errno, strerror(errno));
83 		/* failed, kick parent to finish */
84 		return EXIT_NO_RESOURCE;
85 	}
86 
87 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
88 
89 	do {
90 		int i, n;
91 		struct ifconf ifc;
92 
93 		/* Get list of transport layer addresses */
94 		(void)memset(&ifc, 0, sizeof(ifc));
95 		rc = ioctl(fd, SIOCGIFCONF, &ifc);
96 		if (rc < 0) {
97 			pr_fail("%s: ioctl SIOCGIFCONF failed, errno=%d (%s)\n",
98 				args->name, errno, strerror(errno));
99 			rc = EXIT_FAILURE;
100 			break;
101 		}
102 
103 		/* Do we have any? We should normally have at least lo */
104 		n = ifc.ifc_len / (int)sizeof(struct ifreq);
105 		if (!n) {
106 			if (args->instance == 0)
107 				pr_dbg_skip("%s: no network interfaces found, skipping.\n",
108 					args->name);
109 			break;
110 		}
111 
112 		/* Allocate buffer for the addresses */
113 		ifc.ifc_buf = malloc((size_t)ifc.ifc_len);
114 		if (!ifc.ifc_buf) {
115 			pr_fail("%s: out of memory allocating interface buffer\n",
116 				args->name);
117 			rc = EXIT_NO_RESOURCE;
118 		}
119 
120 		/* Fetch the addresses */
121 		if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
122 			pr_fail("%s: ioctl SIOCGIFCONF failed, errno=%d (%s)\n",
123 				args->name, errno, strerror(errno));
124 			rc = EXIT_FAILURE;
125 			break;
126 		}
127 
128 		/* And get info on each network device */
129 		for (i = 0; i < n; i++) {
130 			struct ifreq *ifr = &ifc.ifc_req[i];
131 
132 #if defined(SIOCGIFINDEX)
133 			/* We got the name, check it's index */
134 			if (ioctl(fd, SIOCGIFINDEX, ifr) < 0)
135 				continue;
136 #endif
137 
138 #if defined(SIOCGIFNAME)
139 			ifr->ifr_ifindex = i;
140 			/* Get name */
141 			if (ioctl(fd, SIOCGIFNAME, ifr) < 0)
142 				continue;
143 
144 			/* Check index is sane */
145 			if (ifr->ifr_ifindex != i) {
146 				pr_fail("%s: interface '%s' returned index %d, expected %d\n",
147 					args->name, ifr->ifr_name,
148 					ifr->ifr_ifindex, i);
149 			}
150 #endif
151 
152 #if defined(SIOCGIFFLAGS)
153 			/* Get flags */
154 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFFLAGS);
155 #endif
156 
157 #if defined(SIOCGIFPFLAGS)
158 			/* Get extended flags */
159 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFPFLAGS);
160 #endif
161 
162 #if defined(SIOCGIFADDR)
163 			/* Get address */
164 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFADDR);
165 #endif
166 
167 #if defined(SIOCGIFNETMASK)
168 			/* Get netmask */
169 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFNETMASK);
170 #endif
171 
172 #if defined(SIOCGIFMETRIC)
173 			/* Get metric (currently not supported) */
174 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFMETRIC);
175 #endif
176 
177 #if defined(SIOCGIFMTU)
178 			/* Get the MTU */
179 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFMTU);
180 #endif
181 
182 #if defined(SIOCGIFHWADDR)
183 			/* Get the hardware address */
184 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFHWADDR);
185 #endif
186 
187 #if defined(SIOCGIFMAP)
188 			/* Get the hardware parameters */
189 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFMAP);
190 #endif
191 
192 #if defined(SIOCGIFTXQLEN)
193 			/* Get the transmit queue length */
194 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFTXQLEN);
195 #endif
196 
197 #if defined(SIOCGIFDSTADDR)
198 			/* Get the destination address */
199 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFDSTADDR);
200 #endif
201 
202 #if defined(SIOCGIFBRDADDR)
203 			/* Get the broadcast address */
204 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFBRDADDR);
205 #endif
206 #if defined(SIOCGMIIPHY) && 0
207 			/* Get from current PHY, disabled for now */
208 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGMIIPHY);
209 #endif
210 #if defined(SIOCGMIIREG) && 0
211 			/* Get reg, disabled for now */
212 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGMIIREG);
213 #endif
214 #if defined(SIOCSIFFLAGS) && 0
215 			/* Get flags, disabled for now */
216 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCSIFFLAGS);
217 #endif
218 #if defined(SIOCSIFMETRIC) && 0
219 			/* Get metric, disabled for now */
220 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCSIFMETRIC);
221 #endif
222 #if defined(SIOCGIFMEM)
223 			/* Get memory space, not implemented */
224 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFMEM);
225 #endif
226 #if defined(SIOCGIFLINK)
227 			/* Get if link, not implemented */
228 			STRESS_NETDEV_CHECK(args, ifr, fd, SIOCGIFLINK);
229 #endif
230 		}
231 		free(ifc.ifc_buf);
232 		inc_counter(args);
233 	} while (keep_stressing(args));
234 
235 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
236 
237 	(void)close(fd);
238 
239 	return rc;
240 }
241 
242 stressor_info_t stress_netdev_info = {
243 	.stressor = stress_netdev,
244 	.class = CLASS_NETWORK,
245 	.help = help
246 };
247 #else
248 stressor_info_t stress_netdev_info = {
249 	.stressor = stress_not_implemented,
250 	.class = CLASS_NETWORK,
251 	.help = help
252 };
253 #endif
254