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