• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2021 Linux Test Project
4  */
5 
6 #include <asm/types.h>
7 #include <linux/netlink.h>
8 #include <linux/veth.h>
9 #include <sys/socket.h>
10 #include <net/if.h>
11 #include "lapi/rtnetlink.h"
12 
13 #define TST_NO_DEFAULT_MAIN
14 #include "tst_test.h"
15 #include "tst_rtnetlink.h"
16 #include "tst_netdevice.h"
17 
create_request(const char * file,const int lineno,unsigned int type,unsigned int flags,const void * payload,size_t psize)18 static struct tst_rtnl_context *create_request(const char *file,
19 	const int lineno, unsigned int type, unsigned int flags,
20 	const void *payload, size_t psize)
21 {
22 	struct tst_rtnl_context *ctx;
23 	struct nlmsghdr header = {
24 		.nlmsg_type = type,
25 		.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags,
26 	};
27 
28 	ctx = tst_rtnl_create_context(file, lineno);
29 
30 	if (!ctx)
31 		return NULL;
32 
33 	if (!tst_rtnl_add_message(file, lineno, ctx, &header, payload, psize)) {
34 		tst_rtnl_destroy_context(file, lineno, ctx);
35 		return NULL;
36 	}
37 
38 	return ctx;
39 }
40 
tst_netdev_index_by_name(const char * file,const int lineno,const char * ifname)41 int tst_netdev_index_by_name(const char *file, const int lineno,
42 	const char *ifname)
43 {
44 	struct ifreq ifr;
45 	int sock, ret;
46 
47 	if (strlen(ifname) >= IFNAMSIZ) {
48 		tst_brk_(file, lineno, TBROK,
49 			"Network device name \"%s\" too long", ifname);
50 		return -1;
51 	}
52 
53 	sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0);
54 
55 	if (sock < 0)
56 		return -1;
57 
58 	strcpy(ifr.ifr_name, ifname);
59 	ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFINDEX, &ifr);
60 	safe_close(file, lineno, NULL, sock);
61 
62 	return ret ? -1 : ifr.ifr_ifindex;
63 }
64 
tst_netdev_set_state(const char * file,const int lineno,const char * ifname,int up)65 int tst_netdev_set_state(const char *file, const int lineno,
66 	const char *ifname, int up)
67 {
68 	struct ifreq ifr;
69 	int sock, ret;
70 
71 	if (strlen(ifname) >= IFNAMSIZ) {
72 		tst_brk_(file, lineno, TBROK,
73 			"Network device name \"%s\" too long", ifname);
74 		return -1;
75 	}
76 
77 	sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0);
78 
79 	if (sock < 0)
80 		return -1;
81 
82 	strcpy(ifr.ifr_name, ifname);
83 	ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFFLAGS, &ifr);
84 
85 	if (ret) {
86 		safe_close(file, lineno, NULL, sock);
87 		return ret;
88 	}
89 
90 	if (up)
91 		ifr.ifr_flags |= IFF_UP;
92 	else
93 		ifr.ifr_flags &= ~IFF_UP;
94 
95 	ret = SAFE_IOCTL_(file, lineno, sock, SIOCSIFFLAGS, &ifr);
96 	safe_close(file, lineno, NULL, sock);
97 
98 	return ret;
99 }
100 
tst_create_veth_pair(const char * file,const int lineno,const char * ifname1,const char * ifname2)101 int tst_create_veth_pair(const char *file, const int lineno,
102 	const char *ifname1, const char *ifname2)
103 {
104 	int ret;
105 	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
106 	struct tst_rtnl_context *ctx;
107 	struct tst_rtnl_attr_list peerinfo[] = {
108 		{IFLA_IFNAME, ifname2, strlen(ifname2) + 1, NULL},
109 		{0, NULL, -1, NULL}
110 	};
111 	struct tst_rtnl_attr_list peerdata[] = {
112 		{VETH_INFO_PEER, &info, sizeof(info), peerinfo},
113 		{0, NULL, -1, NULL}
114 	};
115 	struct tst_rtnl_attr_list attrs[] = {
116 		{IFLA_IFNAME, ifname1, strlen(ifname1) + 1, NULL},
117 		{IFLA_LINKINFO, NULL, 0, (const struct tst_rtnl_attr_list[]){
118 			{IFLA_INFO_KIND, "veth", 4, NULL},
119 			{IFLA_INFO_DATA, NULL, 0, peerdata},
120 			{0, NULL, -1, NULL}
121 		}},
122 		{0, NULL, -1, NULL}
123 	};
124 
125 	if (strlen(ifname1) >= IFNAMSIZ) {
126 		tst_brk_(file, lineno, TBROK,
127 			"Network device name \"%s\" too long", ifname1);
128 		return 0;
129 	}
130 
131 	if (strlen(ifname2) >= IFNAMSIZ) {
132 		tst_brk_(file, lineno, TBROK,
133 			"Network device name \"%s\" too long", ifname2);
134 		return 0;
135 	}
136 
137 	ctx = create_request(file, lineno, RTM_NEWLINK,
138 		NLM_F_CREATE | NLM_F_EXCL, &info, sizeof(info));
139 
140 	if (!ctx)
141 		return 0;
142 
143 	if (tst_rtnl_add_attr_list(file, lineno, ctx, attrs) != 2) {
144 		tst_rtnl_destroy_context(file, lineno, ctx);
145 		return 0;
146 	}
147 
148 	ret = tst_rtnl_send_validate(file, lineno, ctx);
149 	tst_rtnl_destroy_context(file, lineno, ctx);
150 
151 	if (!ret) {
152 		tst_brk_(file, lineno, TBROK | TTERRNO,
153 			"Failed to create veth interfaces %s+%s", ifname1,
154 			ifname2);
155 	}
156 
157 	return ret;
158 }
159 
tst_remove_netdev(const char * file,const int lineno,const char * ifname)160 int tst_remove_netdev(const char *file, const int lineno, const char *ifname)
161 {
162 	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
163 	struct tst_rtnl_context *ctx;
164 	int ret;
165 
166 	if (strlen(ifname) >= IFNAMSIZ) {
167 		tst_brk_(file, lineno, TBROK,
168 			"Network device name \"%s\" too long", ifname);
169 		return 0;
170 	}
171 
172 	ctx = create_request(file, lineno, RTM_DELLINK, 0, &info, sizeof(info));
173 
174 	if (!ctx)
175 		return 0;
176 
177 	if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) {
178 		tst_rtnl_destroy_context(file, lineno, ctx);
179 		return 0;
180 	}
181 
182 	ret = tst_rtnl_send_validate(file, lineno, ctx);
183 	tst_rtnl_destroy_context(file, lineno, ctx);
184 
185 	if (!ret) {
186 		tst_brk_(file, lineno, TBROK | TTERRNO,
187 			"Failed to remove netdevice %s", ifname);
188 	}
189 
190 	return ret;
191 }
192 
modify_address(const char * file,const int lineno,unsigned int action,unsigned int nl_flags,const char * ifname,unsigned int family,const void * address,unsigned int prefix,size_t addrlen,uint32_t addr_flags)193 static int modify_address(const char *file, const int lineno,
194 	unsigned int action, unsigned int nl_flags, const char *ifname,
195 	unsigned int family, const void *address, unsigned int prefix,
196 	size_t addrlen, uint32_t addr_flags)
197 {
198 	struct tst_rtnl_context *ctx;
199 	int index, ret;
200 	struct ifaddrmsg info = {
201 		.ifa_family = family,
202 		.ifa_prefixlen = prefix
203 	};
204 
205 	index = tst_netdev_index_by_name(file, lineno, ifname);
206 
207 	if (index < 0) {
208 		tst_brk_(file, lineno, TBROK, "Interface %s not found", ifname);
209 		return 0;
210 	}
211 
212 	info.ifa_index = index;
213 	ctx = create_request(file, lineno, action, nl_flags, &info,
214 		sizeof(info));
215 
216 	if (!ctx)
217 		return 0;
218 
219 	if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_FLAGS, &addr_flags,
220 		sizeof(uint32_t))) {
221 		tst_rtnl_destroy_context(file, lineno, ctx);
222 		return 0;
223 	}
224 
225 	if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_LOCAL, address,
226 		addrlen)) {
227 		tst_rtnl_destroy_context(file, lineno, ctx);
228 		return 0;
229 	}
230 
231 	ret = tst_rtnl_send_validate(file, lineno, ctx);
232 	tst_rtnl_destroy_context(file, lineno, ctx);
233 
234 	if (!ret) {
235 		tst_brk_(file, lineno, TBROK | TTERRNO,
236 			"Failed to modify %s network address", ifname);
237 	}
238 
239 	return ret;
240 }
241 
tst_netdev_add_address(const char * file,const int lineno,const char * ifname,unsigned int family,const void * address,unsigned int prefix,size_t addrlen,unsigned int flags)242 int tst_netdev_add_address(const char *file, const int lineno,
243 	const char *ifname, unsigned int family, const void *address,
244 	unsigned int prefix, size_t addrlen, unsigned int flags)
245 {
246 	return modify_address(file, lineno, RTM_NEWADDR,
247 		NLM_F_CREATE | NLM_F_EXCL, ifname, family, address, prefix,
248 		addrlen, flags);
249 }
250 
tst_netdev_add_address_inet(const char * file,const int lineno,const char * ifname,in_addr_t address,unsigned int prefix,unsigned int flags)251 int tst_netdev_add_address_inet(const char *file, const int lineno,
252 	const char *ifname, in_addr_t address, unsigned int prefix,
253 	unsigned int flags)
254 {
255 	return tst_netdev_add_address(file, lineno, ifname, AF_INET,
256 		&address, prefix, sizeof(address), flags);
257 }
258 
tst_netdev_remove_address(const char * file,const int lineno,const char * ifname,unsigned int family,const void * address,size_t addrlen)259 int tst_netdev_remove_address(const char *file, const int lineno,
260 	const char *ifname, unsigned int family, const void *address,
261 	size_t addrlen)
262 {
263 	return modify_address(file, lineno, RTM_DELADDR, 0, ifname, family,
264 		address, 0, addrlen, 0);
265 }
266 
tst_netdev_remove_address_inet(const char * file,const int lineno,const char * ifname,in_addr_t address)267 int tst_netdev_remove_address_inet(const char *file, const int lineno,
268 	const char *ifname, in_addr_t address)
269 {
270 	return tst_netdev_remove_address(file, lineno, ifname, AF_INET,
271 		&address, sizeof(address));
272 }
273 
change_ns(const char * file,const int lineno,const char * ifname,unsigned short attr,uint32_t value)274 static int change_ns(const char *file, const int lineno, const char *ifname,
275 	unsigned short attr, uint32_t value)
276 {
277 	struct ifinfomsg info = { .ifi_family = AF_UNSPEC };
278 	struct tst_rtnl_context *ctx;
279 	int ret;
280 
281 	if (strlen(ifname) >= IFNAMSIZ) {
282 		tst_brk_(file, lineno, TBROK,
283 			"Network device name \"%s\" too long", ifname);
284 		return 0;
285 	}
286 
287 	ctx = create_request(file, lineno, RTM_NEWLINK, 0, &info, sizeof(info));
288 
289 	if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) {
290 		tst_rtnl_destroy_context(file, lineno, ctx);
291 		return 0;
292 	}
293 
294 	if (!tst_rtnl_add_attr(file, lineno, ctx, attr, &value,
295 		sizeof(uint32_t))) {
296 		tst_rtnl_destroy_context(file, lineno, ctx);
297 		return 0;
298 	}
299 
300 	ret = tst_rtnl_send_validate(file, lineno, ctx);
301 	tst_rtnl_destroy_context(file, lineno, ctx);
302 
303 	if (!ret) {
304 		tst_brk_(file, lineno, TBROK | TTERRNO,
305 			"Failed to move %s to another namespace", ifname);
306 	}
307 
308 	return ret;
309 }
310 
tst_netdev_change_ns_fd(const char * file,const int lineno,const char * ifname,int nsfd)311 int tst_netdev_change_ns_fd(const char *file, const int lineno,
312 	const char *ifname, int nsfd)
313 {
314 	return change_ns(file, lineno, ifname, IFLA_NET_NS_FD, nsfd);
315 }
316 
tst_netdev_change_ns_pid(const char * file,const int lineno,const char * ifname,pid_t nspid)317 int tst_netdev_change_ns_pid(const char *file, const int lineno,
318 	const char *ifname, pid_t nspid)
319 {
320 	return change_ns(file, lineno, ifname, IFLA_NET_NS_PID, nspid);
321 }
322 
modify_route(const char * file,const int lineno,unsigned int action,unsigned int flags,const char * ifname,unsigned int family,const void * srcaddr,unsigned int srcprefix,size_t srclen,const void * dstaddr,unsigned int dstprefix,size_t dstlen,const void * gateway,size_t gatewaylen)323 static int modify_route(const char *file, const int lineno, unsigned int action,
324 	unsigned int flags, const char *ifname, unsigned int family,
325 	const void *srcaddr, unsigned int srcprefix, size_t srclen,
326 	const void *dstaddr, unsigned int dstprefix, size_t dstlen,
327 	const void *gateway, size_t gatewaylen)
328 {
329 	struct tst_rtnl_context *ctx;
330 	int ret;
331 	int32_t index;
332 	struct rtmsg info = {
333 		.rtm_family = family,
334 		.rtm_dst_len = dstprefix,
335 		.rtm_src_len = srcprefix,
336 		.rtm_table = RT_TABLE_MAIN,
337 		.rtm_protocol = RTPROT_STATIC,
338 		.rtm_type = RTN_UNICAST
339 	};
340 
341 	if (!ifname && !gateway) {
342 		tst_brk_(file, lineno, TBROK,
343 			"Interface name or gateway address required");
344 		return 0;
345 	}
346 
347 	if (ifname && strlen(ifname) >= IFNAMSIZ) {
348 		tst_brk_(file, lineno, TBROK,
349 			"Network device name \"%s\" too long", ifname);
350 		return 0;
351 	}
352 
353 	if (ifname) {
354 		index = tst_netdev_index_by_name(file, lineno, ifname);
355 
356 		if (index < 0)
357 			return 0;
358 	}
359 
360 	if (action == RTM_DELROUTE)
361 		info.rtm_scope = RT_SCOPE_NOWHERE;
362 	else
363 		info.rtm_scope = RT_SCOPE_UNIVERSE;
364 
365 	ctx = create_request(file, lineno, action, flags, &info, sizeof(info));
366 
367 	if (srcaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_SRC, srcaddr,
368 		srclen)) {
369 		tst_rtnl_destroy_context(file, lineno, ctx);
370 		return 0;
371 	}
372 
373 	if (dstaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_DST, dstaddr,
374 		dstlen)) {
375 		tst_rtnl_destroy_context(file, lineno, ctx);
376 		return 0;
377 	}
378 
379 	if (gateway && !tst_rtnl_add_attr(file, lineno, ctx, RTA_GATEWAY,
380 		gateway, gatewaylen)) {
381 		tst_rtnl_destroy_context(file, lineno, ctx);
382 		return 0;
383 	}
384 
385 	if (ifname && !tst_rtnl_add_attr(file, lineno, ctx, RTA_OIF, &index,
386 		sizeof(index))) {
387 		tst_rtnl_destroy_context(file, lineno, ctx);
388 		return 0;
389 	}
390 
391 	ret = tst_rtnl_send_validate(file, lineno, ctx);
392 	tst_rtnl_destroy_context(file, lineno, ctx);
393 
394 	if (!ret) {
395 		tst_brk_(file, lineno, TBROK | TTERRNO,
396 			"Failed to modify network route");
397 	}
398 
399 	return ret;
400 }
401 
modify_route_inet(const char * file,const int lineno,unsigned int action,unsigned int flags,const char * ifname,in_addr_t srcaddr,unsigned int srcprefix,in_addr_t dstaddr,unsigned int dstprefix,in_addr_t gateway)402 static int modify_route_inet(const char *file, const int lineno,
403 	unsigned int action, unsigned int flags, const char *ifname,
404 	in_addr_t srcaddr, unsigned int srcprefix, in_addr_t dstaddr,
405 	unsigned int dstprefix, in_addr_t gateway)
406 {
407 	void *src = NULL, *dst = NULL, *gw = NULL;
408 	size_t srclen = 0, dstlen = 0, gwlen = 0;
409 
410 	if (srcprefix) {
411 		src = &srcaddr;
412 		srclen = sizeof(srcaddr);
413 	}
414 
415 	if (dstprefix) {
416 		dst = &dstaddr;
417 		dstlen = sizeof(dstaddr);
418 	}
419 
420 	if (gateway) {
421 		gw = &gateway;
422 		gwlen = sizeof(gateway);
423 	}
424 
425 	return modify_route(file, lineno, action, flags, ifname, AF_INET, src,
426 		srcprefix, srclen, dst, dstprefix, dstlen, gw, gwlen);
427 }
428 
tst_netdev_add_route(const char * file,const int lineno,const char * ifname,unsigned int family,const void * srcaddr,unsigned int srcprefix,size_t srclen,const void * dstaddr,unsigned int dstprefix,size_t dstlen,const void * gateway,size_t gatewaylen)429 int tst_netdev_add_route(const char *file, const int lineno,
430 	const char *ifname, unsigned int family, const void *srcaddr,
431 	unsigned int srcprefix, size_t srclen, const void *dstaddr,
432 	unsigned int dstprefix, size_t dstlen, const void *gateway,
433 	size_t gatewaylen)
434 {
435 	return modify_route(file, lineno, RTM_NEWROUTE,
436 		NLM_F_CREATE | NLM_F_EXCL, ifname, family, srcaddr, srcprefix,
437 		srclen, dstaddr, dstprefix, dstlen, gateway, gatewaylen);
438 }
439 
tst_netdev_add_route_inet(const char * file,const int lineno,const char * ifname,in_addr_t srcaddr,unsigned int srcprefix,in_addr_t dstaddr,unsigned int dstprefix,in_addr_t gateway)440 int tst_netdev_add_route_inet(const char *file, const int lineno,
441 	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
442 	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway)
443 {
444 	return modify_route_inet(file, lineno, RTM_NEWROUTE,
445 		NLM_F_CREATE | NLM_F_EXCL, ifname, srcaddr, srcprefix, dstaddr,
446 		dstprefix, gateway);
447 }
448 
tst_netdev_remove_route(const char * file,const int lineno,const char * ifname,unsigned int family,const void * srcaddr,unsigned int srcprefix,size_t srclen,const void * dstaddr,unsigned int dstprefix,size_t dstlen,const void * gateway,size_t gatewaylen)449 int tst_netdev_remove_route(const char *file, const int lineno,
450 	const char *ifname, unsigned int family, const void *srcaddr,
451 	unsigned int srcprefix, size_t srclen, const void *dstaddr,
452 	unsigned int dstprefix, size_t dstlen, const void *gateway,
453 	size_t gatewaylen)
454 {
455 	return modify_route(file, lineno, RTM_DELROUTE, 0, ifname, family,
456 		srcaddr, srcprefix, srclen, dstaddr, dstprefix, dstlen,
457 		gateway, gatewaylen);
458 }
459 
tst_netdev_remove_route_inet(const char * file,const int lineno,const char * ifname,in_addr_t srcaddr,unsigned int srcprefix,in_addr_t dstaddr,unsigned int dstprefix,in_addr_t gateway)460 int tst_netdev_remove_route_inet(const char *file, const int lineno,
461 	const char *ifname, in_addr_t srcaddr, unsigned int srcprefix,
462 	in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway)
463 {
464 	return modify_route_inet(file, lineno, RTM_DELROUTE, 0, ifname,
465 		srcaddr, srcprefix, dstaddr, dstprefix, gateway);
466 }
467