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