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