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