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