1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 Petr Vorel <pvorel@suse.cz>
4 */
5
6 #include "config.h"
7 #include "tst_test.h"
8
9 #ifdef HAVE_LIBMNL
10
11 #include <string.h>
12
13 #include <libmnl/libmnl.h>
14 #include <linux/rtnetlink.h>
15 #include <net/if.h>
16 #include <netdb.h>
17 #include <netinet/in.h>
18
19 #include "tst_net.h"
20 #include "tst_safe_net.h"
21 #include "tst_safe_stdio.h"
22
23 #define IP_ADDR_DELIM ','
24
25 static char *c_opt, *d_opt, *g_opt, *ipv6_opt, *p_opt, *r_opt;
26
27 static int family = AF_INET;
28 static int fd, num_loops, port;
29
30 static unsigned int is_ipv6, max, prefix;
31
32 static struct mnl_socket *nl;
33 static struct addrinfo hints;
34
35 struct iface {
36 unsigned int index;
37 struct iface *next;
38 char iface[IFNAMSIZ];
39 };
40
41 struct ip_addr {
42 struct addrinfo *ip;
43 struct ip_addr *next;
44 char ip_str[INET6_ADDRSTRLEN];
45 };
46
47 static struct ip_addr *dst, *gw, *rhost;
48 static struct iface *iface;
49 static unsigned int gw_len, iface_len, rhost_len;
50
save_iface(void ** data,const char * item)51 void save_iface(void **data, const char *item)
52 {
53 struct iface *n = SAFE_MALLOC(sizeof(*n));
54 struct iface **list = (struct iface**)data;
55
56 strncpy(n->iface, item, sizeof(n->iface));
57 n->iface[sizeof(n->iface)-1] = '\0';
58
59 n->index = if_nametoindex(item);
60 if (!n->index)
61 tst_brk(TBROK, "if_nametoindex failed, '%s' not found", item);
62 n->next = *list;
63 *list = n;
64 }
65
save_ip(void ** data,const char * item)66 void save_ip(void **data, const char *item)
67 {
68 struct ip_addr *n = SAFE_MALLOC(sizeof(*n));
69 struct ip_addr **list = (struct ip_addr**)data;
70
71 strncpy(n->ip_str, item, sizeof(n->ip_str));
72 n->ip_str[sizeof(n->ip_str)-1] = '\0';
73
74 SAFE_GETADDRINFO(item, p_opt, &hints, &n->ip);
75 n->next = *list;
76 *list = n;
77 }
78
save_item(void ** list,char * item,void (* callback)(void **,const char *))79 int save_item(void **list, char *item, void (*callback)(void **, const char *))
80 {
81 int len = 0;
82
83 while ((item = strtok(item, TST_TO_STR(IP_ADDR_DELIM))) != NULL) {
84 callback(list, item);
85 item = NULL;
86 len++;
87 }
88
89 return len;
90 }
91
setup(void)92 static void setup(void)
93 {
94 prefix = 24;
95 if (ipv6_opt) {
96 family = AF_INET6;
97 is_ipv6 = 1;
98 prefix = 64;
99 }
100
101 if (!c_opt)
102 tst_brk(TBROK, "missing number of loops (-c num)");
103
104 if (!d_opt)
105 tst_brk(TBROK, "missing iface (-d iface)");
106
107 if (!p_opt)
108 tst_brk(TBROK, "missing rhost port (-p port)");
109
110 if (!r_opt)
111 tst_brk(TBROK, "missing rhost IP (-r IP)");
112
113 if (tst_parse_int(p_opt, &port, 1, 65535))
114 tst_brk(TBROK, "invalid rhost port '%s'", p_opt);
115
116 if (tst_parse_int(c_opt, &num_loops, 1, INT_MAX)) {
117 num_loops = INT_MAX;
118 tst_res(TWARN, "invalid number of loops (-c %s), using: %d",
119 c_opt, num_loops);
120 }
121
122 iface_len = save_item((void **)&iface, d_opt, save_iface);
123 rhost_len = save_item((void **)&rhost, r_opt, save_ip);
124
125 max = MAX(iface_len, rhost_len);
126 if (iface_len > 1 && rhost_len > 1 && iface_len != max)
127 tst_brk(TBROK, "-d specifies more NICs and -r more IPs, they need to have the same count");
128
129 if (g_opt) {
130 gw_len = save_item((void **)&gw, g_opt, save_ip);
131 max = MAX(gw_len, max);
132
133 if (gw_len > 1 && max > 1 && gw_len != max) {
134 if (iface_len == max)
135 tst_brk(TBROK, "-d specifies more NICs and -r more IPs, they need to have the same count");
136 else
137 tst_brk(TBROK, "-g and -r specify more IP, they need to have the same count");
138 }
139 }
140
141 struct ip_addr *p_rhost = rhost;
142
143 while (p_rhost) {
144 char dst_str[INET6_ADDRSTRLEN];
145
146 if (!strncpy(dst_str, p_rhost->ip_str, sizeof(dst_str)))
147 tst_brk(TBROK, "failed copy IP '%s'", p_rhost->ip_str);
148 dst_str[strlen(p_rhost->ip_str)-1] = '\0';
149
150 if (!strcat(dst_str, "0"))
151 tst_brk(TBROK, "strcat failed: '%s'", dst_str);
152
153 save_ip((void **)&dst, dst_str);
154 p_rhost = p_rhost->next;
155 }
156
157 fd = SAFE_SOCKET(family, SOCK_DGRAM, IPPROTO_UDP);
158
159 memset(&hints, 0, sizeof(struct addrinfo));
160 hints.ai_family = family;
161 hints.ai_socktype = SOCK_DGRAM;
162 hints.ai_flags = 0;
163 hints.ai_protocol = 0;
164 hints.ai_addr = INADDR_ANY;
165 }
166
cleanup(void)167 static void cleanup(void)
168 {
169 if (fd > 0)
170 close(fd);
171
172 if (nl)
173 mnl_socket_close(nl);
174 }
175
brk_on_route_error(const char * msg,int iface,struct sockaddr * dst,struct sockaddr * gw,int type)176 static void brk_on_route_error(const char *msg, int iface,
177 struct sockaddr *dst, struct sockaddr *gw, int type)
178 {
179 char dst_str[INET6_ADDRSTRLEN], gw_str[INET6_ADDRSTRLEN];
180 tst_sock_addr(dst, sizeof(dst), dst_str, sizeof(dst_str));
181 if (gw)
182 tst_sock_addr(gw, sizeof(gw), gw_str, sizeof(gw_str));
183
184 tst_res(TINFO, "type: %s, iface: %d, dst: %s, gw: %s",
185 type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
186 iface, dst_str, gw ? gw_str : "null");
187 tst_brk(TBROK, "%s failed (errno=%d): %s", msg, errno, strerror(errno));
188 }
189
rtnl_route(int iface,struct addrinfo * dst,struct addrinfo * gw,int type)190 static void rtnl_route(int iface, struct addrinfo *dst, struct addrinfo *gw,
191 int type)
192 {
193 struct mnl_socket *nl;
194 char buf[MNL_SOCKET_BUFFER_SIZE];
195 struct nlmsghdr *nlh;
196 struct rtmsg *rtm;
197 uint32_t seq, portid;
198 struct in6_addr dst_in6, gw_in6;
199 in_addr_t dst_ip, gw_ip;
200 int ret;
201
202 nlh = mnl_nlmsg_put_header(buf);
203 nlh->nlmsg_type = type;
204
205 nlh->nlmsg_flags = NLM_F_ACK;
206 if (type == RTM_NEWROUTE)
207 nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE;
208
209 nlh->nlmsg_seq = seq = time(NULL);
210
211 rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
212 rtm->rtm_family = family;
213 rtm->rtm_dst_len = prefix;
214 rtm->rtm_src_len = 0;
215 rtm->rtm_tos = 0;
216 rtm->rtm_protocol = RTPROT_STATIC;
217 rtm->rtm_table = RT_TABLE_MAIN;
218 rtm->rtm_type = RTN_UNICAST;
219 rtm->rtm_scope = gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
220 rtm->rtm_flags = 0;
221
222 if (is_ipv6) {
223 dst_in6 = ((struct sockaddr_in6 *)dst->ai_addr)->sin6_addr;
224 mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst_in6);
225 } else {
226 dst_ip = ((struct sockaddr_in *)dst->ai_addr)->sin_addr.s_addr;
227 mnl_attr_put_u32(nlh, RTA_DST, dst_ip);
228 }
229
230 mnl_attr_put_u32(nlh, RTA_OIF, iface);
231
232 if (gw) {
233 if (is_ipv6) {
234 gw_in6 = ((struct sockaddr_in6 *)gw->ai_addr)->sin6_addr;
235 mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), &gw_in6);
236 } else {
237 gw_ip = ((struct sockaddr_in *)gw->ai_addr)->sin_addr.s_addr;
238 mnl_attr_put_u32(nlh, RTA_GATEWAY, gw_ip);
239 }
240 }
241
242 nl = mnl_socket_open(NETLINK_ROUTE);
243 if (nl == NULL)
244 brk_on_route_error("mnl_socket_open", iface, dst->ai_addr, gw ?
245 gw->ai_addr : NULL, type);
246
247 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
248 brk_on_route_error("mnl_socket_bind", iface, dst->ai_addr, gw ?
249 gw->ai_addr : NULL, type);
250
251 portid = mnl_socket_get_portid(nl);
252
253 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
254 brk_on_route_error("mnl_socket_sendto", iface, dst->ai_addr, gw
255 ? gw->ai_addr : NULL, type);
256
257 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
258 if (ret < 0)
259 brk_on_route_error("mnl_socket_recvfrom", iface, dst->ai_addr,
260 gw ? gw->ai_addr : NULL, type);
261
262 ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
263 if (ret < 0)
264 brk_on_route_error("mnl_cb_run", iface, dst->ai_addr, gw ?
265 gw->ai_addr : NULL, type);
266
267 mnl_socket_close(nl);
268 }
269
send_udp(struct addrinfo * rhost_addrinfo)270 static void send_udp(struct addrinfo *rhost_addrinfo)
271 {
272 const char *msg = "foo";
273
274 SAFE_SENDTO(1, fd, msg, sizeof(msg), MSG_CONFIRM,
275 rhost_addrinfo->ai_addr, rhost_addrinfo->ai_addrlen);
276 }
277
run(void)278 static void run(void)
279 {
280 int i;
281
282 tst_res(TINFO, "adding and deleting route %d times", num_loops);
283
284 struct ip_addr *p_dst = dst, *p_gw = gw, *p_rhost = rhost;
285 struct iface *p_iface = iface;
286
287 for (i = 0; i < num_loops; i++) {
288 rtnl_route(p_iface->index, p_dst->ip, gw ? p_gw->ip : NULL,
289 RTM_NEWROUTE);
290 send_udp(p_rhost->ip);
291 rtnl_route(p_iface->index, p_dst->ip, gw ? p_gw->ip : NULL,
292 RTM_DELROUTE);
293
294 if (gw)
295 p_gw = p_gw->next ?: gw;
296 p_dst = p_dst->next ?: dst;
297 p_iface = p_iface->next ?: iface;
298 p_rhost = p_rhost->next ?: rhost;
299 }
300
301 tst_res(TPASS, "routes created and deleted");
302 }
303
304 static struct tst_test test = {
305 .test_all = run,
306 .needs_root = 1,
307 .setup = setup,
308 .cleanup = cleanup,
309 .options = (struct tst_option[]) {
310 {"6", &ipv6_opt, "Use IPv6 (default is IPv4)"},
311 {"c:", &c_opt, "Num loops (mandatory)"},
312 {"d:", &d_opt, "Interface to work on (mandatory)"},
313 {"g:", &g_opt, "Gateway IP"},
314 {"p:", &p_opt, "Rhost port (mandatory)"},
315 {"r:", &r_opt, "Rhost IP (mandatory)\n\n-g, -r IP parameter can contain more IP, separated by "
316 TST_TO_STR(IP_ADDR_DELIM)},
317 {}
318 },
319 };
320 #else
321 TST_TEST_TCONF("libmnl library and headers are required");
322 #endif /* HAVE_LIBMNL */
323