18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* nettest - used for functional tests of networking APIs 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2013-2019 David Ahern <dsahern@gmail.com>. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define _GNU_SOURCE 88c2ecf20Sopenharmony_ci#include <features.h> 98c2ecf20Sopenharmony_ci#include <sys/types.h> 108c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 118c2ecf20Sopenharmony_ci#include <sys/socket.h> 128c2ecf20Sopenharmony_ci#include <linux/tcp.h> 138c2ecf20Sopenharmony_ci#include <arpa/inet.h> 148c2ecf20Sopenharmony_ci#include <net/if.h> 158c2ecf20Sopenharmony_ci#include <netinet/in.h> 168c2ecf20Sopenharmony_ci#include <netdb.h> 178c2ecf20Sopenharmony_ci#include <fcntl.h> 188c2ecf20Sopenharmony_ci#include <libgen.h> 198c2ecf20Sopenharmony_ci#include <limits.h> 208c2ecf20Sopenharmony_ci#include <stdarg.h> 218c2ecf20Sopenharmony_ci#include <stdio.h> 228c2ecf20Sopenharmony_ci#include <stdlib.h> 238c2ecf20Sopenharmony_ci#include <string.h> 248c2ecf20Sopenharmony_ci#include <unistd.h> 258c2ecf20Sopenharmony_ci#include <time.h> 268c2ecf20Sopenharmony_ci#include <errno.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#ifndef IPV6_UNICAST_IF 298c2ecf20Sopenharmony_ci#define IPV6_UNICAST_IF 76 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci#ifndef IPV6_MULTICAST_IF 328c2ecf20Sopenharmony_ci#define IPV6_MULTICAST_IF 17 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DEFAULT_PORT 12345 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifndef MAX 388c2ecf20Sopenharmony_ci#define MAX(a, b) ((a) > (b) ? (a) : (b)) 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci#ifndef MIN 418c2ecf20Sopenharmony_ci#define MIN(a, b) ((a) < (b) ? (a) : (b)) 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct sock_args { 458c2ecf20Sopenharmony_ci /* local address */ 468c2ecf20Sopenharmony_ci union { 478c2ecf20Sopenharmony_ci struct in_addr in; 488c2ecf20Sopenharmony_ci struct in6_addr in6; 498c2ecf20Sopenharmony_ci } local_addr; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* remote address */ 528c2ecf20Sopenharmony_ci union { 538c2ecf20Sopenharmony_ci struct in_addr in; 548c2ecf20Sopenharmony_ci struct in6_addr in6; 558c2ecf20Sopenharmony_ci } remote_addr; 568c2ecf20Sopenharmony_ci int scope_id; /* remote scope; v6 send only */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct in_addr grp; /* multicast group */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci unsigned int has_local_ip:1, 618c2ecf20Sopenharmony_ci has_remote_ip:1, 628c2ecf20Sopenharmony_ci has_grp:1, 638c2ecf20Sopenharmony_ci has_expected_laddr:1, 648c2ecf20Sopenharmony_ci has_expected_raddr:1, 658c2ecf20Sopenharmony_ci bind_test_only:1; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci unsigned short port; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci int type; /* DGRAM, STREAM, RAW */ 708c2ecf20Sopenharmony_ci int protocol; 718c2ecf20Sopenharmony_ci int version; /* AF_INET/AF_INET6 */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci int use_setsockopt; 748c2ecf20Sopenharmony_ci int use_cmsg; 758c2ecf20Sopenharmony_ci const char *dev; 768c2ecf20Sopenharmony_ci int ifindex; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci const char *password; 798c2ecf20Sopenharmony_ci /* prefix for MD5 password */ 808c2ecf20Sopenharmony_ci union { 818c2ecf20Sopenharmony_ci struct sockaddr_in v4; 828c2ecf20Sopenharmony_ci struct sockaddr_in6 v6; 838c2ecf20Sopenharmony_ci } md5_prefix; 848c2ecf20Sopenharmony_ci unsigned int prefix_len; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* expected addresses and device index for connection */ 878c2ecf20Sopenharmony_ci int expected_ifindex; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* local address */ 908c2ecf20Sopenharmony_ci union { 918c2ecf20Sopenharmony_ci struct in_addr in; 928c2ecf20Sopenharmony_ci struct in6_addr in6; 938c2ecf20Sopenharmony_ci } expected_laddr; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* remote address */ 968c2ecf20Sopenharmony_ci union { 978c2ecf20Sopenharmony_ci struct in_addr in; 988c2ecf20Sopenharmony_ci struct in6_addr in6; 998c2ecf20Sopenharmony_ci } expected_raddr; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int server_mode; 1038c2ecf20Sopenharmony_cistatic unsigned int prog_timeout = 5; 1048c2ecf20Sopenharmony_cistatic unsigned int interactive; 1058c2ecf20Sopenharmony_cistatic int iter = 1; 1068c2ecf20Sopenharmony_cistatic char *msg = "Hello world!"; 1078c2ecf20Sopenharmony_cistatic int msglen; 1088c2ecf20Sopenharmony_cistatic int quiet; 1098c2ecf20Sopenharmony_cistatic int try_broadcast = 1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic char *timestamp(char *timebuf, int buflen) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci time_t now; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci now = time(NULL); 1168c2ecf20Sopenharmony_ci if (strftime(timebuf, buflen, "%T", localtime(&now)) == 0) { 1178c2ecf20Sopenharmony_ci memset(timebuf, 0, buflen); 1188c2ecf20Sopenharmony_ci strncpy(timebuf, "00:00:00", buflen-1); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return timebuf; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void log_msg(const char *format, ...) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci char timebuf[64]; 1278c2ecf20Sopenharmony_ci va_list args; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (quiet) 1308c2ecf20Sopenharmony_ci return; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci fprintf(stdout, "%s %s:", 1338c2ecf20Sopenharmony_ci timestamp(timebuf, sizeof(timebuf)), 1348c2ecf20Sopenharmony_ci server_mode ? "server" : "client"); 1358c2ecf20Sopenharmony_ci va_start(args, format); 1368c2ecf20Sopenharmony_ci vfprintf(stdout, format, args); 1378c2ecf20Sopenharmony_ci va_end(args); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci fflush(stdout); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void log_error(const char *format, ...) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci char timebuf[64]; 1458c2ecf20Sopenharmony_ci va_list args; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (quiet) 1488c2ecf20Sopenharmony_ci return; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci fprintf(stderr, "%s %s:", 1518c2ecf20Sopenharmony_ci timestamp(timebuf, sizeof(timebuf)), 1528c2ecf20Sopenharmony_ci server_mode ? "server" : "client"); 1538c2ecf20Sopenharmony_ci va_start(args, format); 1548c2ecf20Sopenharmony_ci vfprintf(stderr, format, args); 1558c2ecf20Sopenharmony_ci va_end(args); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci fflush(stderr); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void log_err_errno(const char *fmt, ...) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci char timebuf[64]; 1638c2ecf20Sopenharmony_ci va_list args; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (quiet) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci fprintf(stderr, "%s %s: ", 1698c2ecf20Sopenharmony_ci timestamp(timebuf, sizeof(timebuf)), 1708c2ecf20Sopenharmony_ci server_mode ? "server" : "client"); 1718c2ecf20Sopenharmony_ci va_start(args, fmt); 1728c2ecf20Sopenharmony_ci vfprintf(stderr, fmt, args); 1738c2ecf20Sopenharmony_ci va_end(args); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci fprintf(stderr, ": %d: %s\n", errno, strerror(errno)); 1768c2ecf20Sopenharmony_ci fflush(stderr); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void log_address(const char *desc, struct sockaddr *sa) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci char addrstr[64]; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (quiet) 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (sa->sa_family == AF_INET) { 1878c2ecf20Sopenharmony_ci struct sockaddr_in *s = (struct sockaddr_in *) sa; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci log_msg("%s %s:%d", 1908c2ecf20Sopenharmony_ci desc, 1918c2ecf20Sopenharmony_ci inet_ntop(AF_INET, &s->sin_addr, addrstr, 1928c2ecf20Sopenharmony_ci sizeof(addrstr)), 1938c2ecf20Sopenharmony_ci ntohs(s->sin_port)); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci } else if (sa->sa_family == AF_INET6) { 1968c2ecf20Sopenharmony_ci struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci log_msg("%s [%s]:%d", 1998c2ecf20Sopenharmony_ci desc, 2008c2ecf20Sopenharmony_ci inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, 2018c2ecf20Sopenharmony_ci sizeof(addrstr)), 2028c2ecf20Sopenharmony_ci ntohs(s6->sin6_port)); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci printf("\n"); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci fflush(stdout); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int keylen = strlen(args->password); 2138c2ecf20Sopenharmony_ci struct tcp_md5sig md5sig = {}; 2148c2ecf20Sopenharmony_ci int opt = TCP_MD5SIG; 2158c2ecf20Sopenharmony_ci int rc; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci md5sig.tcpm_keylen = keylen; 2188c2ecf20Sopenharmony_ci memcpy(md5sig.tcpm_key, args->password, keylen); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (args->prefix_len) { 2218c2ecf20Sopenharmony_ci opt = TCP_MD5SIG_EXT; 2228c2ecf20Sopenharmony_ci md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_PREFIX; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci md5sig.tcpm_prefixlen = args->prefix_len; 2258c2ecf20Sopenharmony_ci addr = &args->md5_prefix; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci memcpy(&md5sig.tcpm_addr, addr, alen); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (args->ifindex) { 2308c2ecf20Sopenharmony_ci opt = TCP_MD5SIG_EXT; 2318c2ecf20Sopenharmony_ci md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_IFINDEX; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci md5sig.tcpm_ifindex = args->ifindex; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rc = setsockopt(sd, IPPROTO_TCP, opt, &md5sig, sizeof(md5sig)); 2378c2ecf20Sopenharmony_ci if (rc < 0) { 2388c2ecf20Sopenharmony_ci /* ENOENT is harmless. Returned when a password is cleared */ 2398c2ecf20Sopenharmony_ci if (errno == ENOENT) 2408c2ecf20Sopenharmony_ci rc = 0; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci log_err_errno("setsockopt(TCP_MD5SIG)"); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return rc; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int tcp_md5_remote(int sd, struct sock_args *args) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct sockaddr_in sin = { 2518c2ecf20Sopenharmony_ci .sin_family = AF_INET, 2528c2ecf20Sopenharmony_ci }; 2538c2ecf20Sopenharmony_ci struct sockaddr_in6 sin6 = { 2548c2ecf20Sopenharmony_ci .sin6_family = AF_INET6, 2558c2ecf20Sopenharmony_ci }; 2568c2ecf20Sopenharmony_ci void *addr; 2578c2ecf20Sopenharmony_ci int alen; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci switch (args->version) { 2608c2ecf20Sopenharmony_ci case AF_INET: 2618c2ecf20Sopenharmony_ci sin.sin_port = htons(args->port); 2628c2ecf20Sopenharmony_ci sin.sin_addr = args->remote_addr.in; 2638c2ecf20Sopenharmony_ci addr = &sin; 2648c2ecf20Sopenharmony_ci alen = sizeof(sin); 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case AF_INET6: 2678c2ecf20Sopenharmony_ci sin6.sin6_port = htons(args->port); 2688c2ecf20Sopenharmony_ci sin6.sin6_addr = args->remote_addr.in6; 2698c2ecf20Sopenharmony_ci addr = &sin6; 2708c2ecf20Sopenharmony_ci alen = sizeof(sin6); 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci default: 2738c2ecf20Sopenharmony_ci log_error("unknown address family\n"); 2748c2ecf20Sopenharmony_ci exit(1); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (tcp_md5sig(sd, addr, alen, args)) 2788c2ecf20Sopenharmony_ci return -1; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int get_ifidx(const char *ifname) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct ifreq ifdata; 2868c2ecf20Sopenharmony_ci int sd, rc; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!ifname || *ifname == '\0') 2898c2ecf20Sopenharmony_ci return -1; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci memset(&ifdata, 0, sizeof(ifdata)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci strcpy(ifdata.ifr_name, ifname); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 2968c2ecf20Sopenharmony_ci if (sd < 0) { 2978c2ecf20Sopenharmony_ci log_err_errno("socket failed"); 2988c2ecf20Sopenharmony_ci return -1; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rc = ioctl(sd, SIOCGIFINDEX, (char *)&ifdata); 3028c2ecf20Sopenharmony_ci close(sd); 3038c2ecf20Sopenharmony_ci if (rc != 0) { 3048c2ecf20Sopenharmony_ci log_err_errno("ioctl(SIOCGIFINDEX) failed"); 3058c2ecf20Sopenharmony_ci return -1; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return ifdata.ifr_ifindex; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int bind_to_device(int sd, const char *name) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int rc; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1); 3168c2ecf20Sopenharmony_ci if (rc < 0) 3178c2ecf20Sopenharmony_ci log_err_errno("setsockopt(SO_BINDTODEVICE)"); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return rc; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int get_bind_to_device(int sd, char *name, size_t len) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int rc; 3258c2ecf20Sopenharmony_ci socklen_t optlen = len; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci name[0] = '\0'; 3288c2ecf20Sopenharmony_ci rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen); 3298c2ecf20Sopenharmony_ci if (rc < 0) 3308c2ecf20Sopenharmony_ci log_err_errno("setsockopt(SO_BINDTODEVICE)"); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return rc; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int check_device(int sd, struct sock_args *args) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci int ifindex = 0; 3388c2ecf20Sopenharmony_ci char name[32]; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (get_bind_to_device(sd, name, sizeof(name))) 3418c2ecf20Sopenharmony_ci *name = '\0'; 3428c2ecf20Sopenharmony_ci else 3438c2ecf20Sopenharmony_ci ifindex = get_ifidx(name); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci log_msg(" bound to device %s/%d\n", 3468c2ecf20Sopenharmony_ci *name ? name : "<none>", ifindex); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!args->expected_ifindex) 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (args->expected_ifindex != ifindex) { 3528c2ecf20Sopenharmony_ci log_error("Device index mismatch: expected %d have %d\n", 3538c2ecf20Sopenharmony_ci args->expected_ifindex, ifindex); 3548c2ecf20Sopenharmony_ci return 1; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci log_msg("Device index matches: expected %d have %d\n", 3588c2ecf20Sopenharmony_ci args->expected_ifindex, ifindex); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int set_pktinfo_v4(int sd) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci int one = 1; 3668c2ecf20Sopenharmony_ci int rc; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_IP, IP_PKTINFO, &one, sizeof(one)); 3698c2ecf20Sopenharmony_ci if (rc < 0 && rc != -ENOTSUP) 3708c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IP_PKTINFO)"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return rc; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int set_recvpktinfo_v6(int sd) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int one = 1; 3788c2ecf20Sopenharmony_ci int rc; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); 3818c2ecf20Sopenharmony_ci if (rc < 0 && rc != -ENOTSUP) 3828c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IPV6_RECVPKTINFO)"); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return rc; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int set_recverr_v4(int sd) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int one = 1; 3908c2ecf20Sopenharmony_ci int rc; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_IP, IP_RECVERR, &one, sizeof(one)); 3938c2ecf20Sopenharmony_ci if (rc < 0 && rc != -ENOTSUP) 3948c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IP_RECVERR)"); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return rc; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int set_recverr_v6(int sd) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int one = 1; 4028c2ecf20Sopenharmony_ci int rc; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one)); 4058c2ecf20Sopenharmony_ci if (rc < 0 && rc != -ENOTSUP) 4068c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IPV6_RECVERR)"); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return rc; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int set_unicast_if(int sd, int ifindex, int version) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int opt = IP_UNICAST_IF; 4148c2ecf20Sopenharmony_ci int level = SOL_IP; 4158c2ecf20Sopenharmony_ci int rc; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ifindex = htonl(ifindex); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (version == AF_INET6) { 4208c2ecf20Sopenharmony_ci opt = IPV6_UNICAST_IF; 4218c2ecf20Sopenharmony_ci level = SOL_IPV6; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci rc = setsockopt(sd, level, opt, &ifindex, sizeof(ifindex)); 4248c2ecf20Sopenharmony_ci if (rc < 0) 4258c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IP_UNICAST_IF)"); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return rc; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int set_multicast_if(int sd, int ifindex) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct ip_mreqn mreq = { .imr_ifindex = ifindex }; 4338c2ecf20Sopenharmony_ci int rc; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci rc = setsockopt(sd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)); 4368c2ecf20Sopenharmony_ci if (rc < 0) 4378c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IP_MULTICAST_IF)"); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return rc; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int set_membership(int sd, uint32_t grp, uint32_t addr, int ifindex) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci uint32_t if_addr = addr; 4458c2ecf20Sopenharmony_ci struct ip_mreqn mreq; 4468c2ecf20Sopenharmony_ci int rc; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (addr == htonl(INADDR_ANY) && !ifindex) { 4498c2ecf20Sopenharmony_ci log_error("Either local address or device needs to be given for multicast membership\n"); 4508c2ecf20Sopenharmony_ci return -1; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mreq.imr_multiaddr.s_addr = grp; 4548c2ecf20Sopenharmony_ci mreq.imr_address.s_addr = if_addr; 4558c2ecf20Sopenharmony_ci mreq.imr_ifindex = ifindex; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci rc = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); 4588c2ecf20Sopenharmony_ci if (rc < 0) { 4598c2ecf20Sopenharmony_ci log_err_errno("setsockopt(IP_ADD_MEMBERSHIP)"); 4608c2ecf20Sopenharmony_ci return -1; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int set_broadcast(int sd) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci unsigned int one = 1; 4698c2ecf20Sopenharmony_ci int rc = 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) != 0) { 4728c2ecf20Sopenharmony_ci log_err_errno("setsockopt(SO_BROADCAST)"); 4738c2ecf20Sopenharmony_ci rc = -1; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return rc; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int set_reuseport(int sd) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci unsigned int one = 1; 4828c2ecf20Sopenharmony_ci int rc = 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != 0) { 4858c2ecf20Sopenharmony_ci log_err_errno("setsockopt(SO_REUSEPORT)"); 4868c2ecf20Sopenharmony_ci rc = -1; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return rc; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int set_reuseaddr(int sd) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci unsigned int one = 1; 4958c2ecf20Sopenharmony_ci int rc = 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0) { 4988c2ecf20Sopenharmony_ci log_err_errno("setsockopt(SO_REUSEADDR)"); 4998c2ecf20Sopenharmony_ci rc = -1; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return rc; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int str_to_uint(const char *str, int min, int max, unsigned int *value) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci int number; 5088c2ecf20Sopenharmony_ci char *end; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci errno = 0; 5118c2ecf20Sopenharmony_ci number = (unsigned int) strtoul(str, &end, 0); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* entire string should be consumed by conversion 5148c2ecf20Sopenharmony_ci * and value should be between min and max 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci if (((*end == '\0') || (*end == '\n')) && (end != str) && 5178c2ecf20Sopenharmony_ci (errno != ERANGE) && (min <= number) && (number <= max)) { 5188c2ecf20Sopenharmony_ci *value = number; 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return -1; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int expected_addr_match(struct sockaddr *sa, void *expected, 5268c2ecf20Sopenharmony_ci const char *desc) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci char addrstr[64]; 5298c2ecf20Sopenharmony_ci int rc = 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (sa->sa_family == AF_INET) { 5328c2ecf20Sopenharmony_ci struct sockaddr_in *s = (struct sockaddr_in *) sa; 5338c2ecf20Sopenharmony_ci struct in_addr *exp_in = (struct in_addr *) expected; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (s->sin_addr.s_addr != exp_in->s_addr) { 5368c2ecf20Sopenharmony_ci log_error("%s address does not match expected %s", 5378c2ecf20Sopenharmony_ci desc, 5388c2ecf20Sopenharmony_ci inet_ntop(AF_INET, exp_in, 5398c2ecf20Sopenharmony_ci addrstr, sizeof(addrstr))); 5408c2ecf20Sopenharmony_ci rc = 1; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } else if (sa->sa_family == AF_INET6) { 5438c2ecf20Sopenharmony_ci struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; 5448c2ecf20Sopenharmony_ci struct in6_addr *exp_in = (struct in6_addr *) expected; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (memcmp(&s6->sin6_addr, exp_in, sizeof(*exp_in))) { 5478c2ecf20Sopenharmony_ci log_error("%s address does not match expected %s", 5488c2ecf20Sopenharmony_ci desc, 5498c2ecf20Sopenharmony_ci inet_ntop(AF_INET6, exp_in, 5508c2ecf20Sopenharmony_ci addrstr, sizeof(addrstr))); 5518c2ecf20Sopenharmony_ci rc = 1; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } else { 5548c2ecf20Sopenharmony_ci log_error("%s address does not match expected - unknown family", 5558c2ecf20Sopenharmony_ci desc); 5568c2ecf20Sopenharmony_ci rc = 1; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!rc) 5608c2ecf20Sopenharmony_ci log_msg("%s address matches expected\n", desc); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci return rc; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int show_sockstat(int sd, struct sock_args *args) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct sockaddr_in6 local_addr, remote_addr; 5688c2ecf20Sopenharmony_ci socklen_t alen = sizeof(local_addr); 5698c2ecf20Sopenharmony_ci struct sockaddr *sa; 5708c2ecf20Sopenharmony_ci const char *desc; 5718c2ecf20Sopenharmony_ci int rc = 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci desc = server_mode ? "server local:" : "client local:"; 5748c2ecf20Sopenharmony_ci sa = (struct sockaddr *) &local_addr; 5758c2ecf20Sopenharmony_ci if (getsockname(sd, sa, &alen) == 0) { 5768c2ecf20Sopenharmony_ci log_address(desc, sa); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (args->has_expected_laddr) { 5798c2ecf20Sopenharmony_ci rc = expected_addr_match(sa, &args->expected_laddr, 5808c2ecf20Sopenharmony_ci "local"); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } else { 5838c2ecf20Sopenharmony_ci log_err_errno("getsockname failed"); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci sa = (struct sockaddr *) &remote_addr; 5878c2ecf20Sopenharmony_ci desc = server_mode ? "server peer:" : "client peer:"; 5888c2ecf20Sopenharmony_ci if (getpeername(sd, sa, &alen) == 0) { 5898c2ecf20Sopenharmony_ci log_address(desc, sa); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (args->has_expected_raddr) { 5928c2ecf20Sopenharmony_ci rc |= expected_addr_match(sa, &args->expected_raddr, 5938c2ecf20Sopenharmony_ci "remote"); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } else { 5968c2ecf20Sopenharmony_ci log_err_errno("getpeername failed"); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return rc; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int get_index_from_cmsg(struct msghdr *m) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct cmsghdr *cm; 6058c2ecf20Sopenharmony_ci int ifindex = 0; 6068c2ecf20Sopenharmony_ci char buf[64]; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(m); 6098c2ecf20Sopenharmony_ci m->msg_controllen != 0 && cm; 6108c2ecf20Sopenharmony_ci cm = (struct cmsghdr *)CMSG_NXTHDR(m, cm)) { 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (cm->cmsg_level == SOL_IP && 6138c2ecf20Sopenharmony_ci cm->cmsg_type == IP_PKTINFO) { 6148c2ecf20Sopenharmony_ci struct in_pktinfo *pi; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci pi = (struct in_pktinfo *)(CMSG_DATA(cm)); 6178c2ecf20Sopenharmony_ci inet_ntop(AF_INET, &pi->ipi_addr, buf, sizeof(buf)); 6188c2ecf20Sopenharmony_ci ifindex = pi->ipi_ifindex; 6198c2ecf20Sopenharmony_ci } else if (cm->cmsg_level == SOL_IPV6 && 6208c2ecf20Sopenharmony_ci cm->cmsg_type == IPV6_PKTINFO) { 6218c2ecf20Sopenharmony_ci struct in6_pktinfo *pi6; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm)); 6248c2ecf20Sopenharmony_ci inet_ntop(AF_INET6, &pi6->ipi6_addr, buf, sizeof(buf)); 6258c2ecf20Sopenharmony_ci ifindex = pi6->ipi6_ifindex; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (ifindex) { 6308c2ecf20Sopenharmony_ci log_msg(" pktinfo: ifindex %d dest addr %s\n", 6318c2ecf20Sopenharmony_ci ifindex, buf); 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci return ifindex; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int send_msg_no_cmsg(int sd, void *addr, socklen_t alen) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci int err; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ciagain: 6418c2ecf20Sopenharmony_ci err = sendto(sd, msg, msglen, 0, addr, alen); 6428c2ecf20Sopenharmony_ci if (err < 0) { 6438c2ecf20Sopenharmony_ci if (errno == EACCES && try_broadcast) { 6448c2ecf20Sopenharmony_ci try_broadcast = 0; 6458c2ecf20Sopenharmony_ci if (!set_broadcast(sd)) 6468c2ecf20Sopenharmony_ci goto again; 6478c2ecf20Sopenharmony_ci errno = EACCES; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci log_err_errno("sendto failed"); 6518c2ecf20Sopenharmony_ci return 1; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int send_msg_cmsg(int sd, void *addr, socklen_t alen, 6588c2ecf20Sopenharmony_ci int ifindex, int version) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci unsigned char cmsgbuf[64]; 6618c2ecf20Sopenharmony_ci struct iovec iov[2]; 6628c2ecf20Sopenharmony_ci struct cmsghdr *cm; 6638c2ecf20Sopenharmony_ci struct msghdr m; 6648c2ecf20Sopenharmony_ci int err; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci iov[0].iov_base = msg; 6678c2ecf20Sopenharmony_ci iov[0].iov_len = msglen; 6688c2ecf20Sopenharmony_ci m.msg_iov = iov; 6698c2ecf20Sopenharmony_ci m.msg_iovlen = 1; 6708c2ecf20Sopenharmony_ci m.msg_name = (caddr_t)addr; 6718c2ecf20Sopenharmony_ci m.msg_namelen = alen; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci memset(cmsgbuf, 0, sizeof(cmsgbuf)); 6748c2ecf20Sopenharmony_ci cm = (struct cmsghdr *)cmsgbuf; 6758c2ecf20Sopenharmony_ci m.msg_control = (caddr_t)cm; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (version == AF_INET) { 6788c2ecf20Sopenharmony_ci struct in_pktinfo *pi; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci cm->cmsg_level = SOL_IP; 6818c2ecf20Sopenharmony_ci cm->cmsg_type = IP_PKTINFO; 6828c2ecf20Sopenharmony_ci cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 6838c2ecf20Sopenharmony_ci pi = (struct in_pktinfo *)(CMSG_DATA(cm)); 6848c2ecf20Sopenharmony_ci pi->ipi_ifindex = ifindex; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci m.msg_controllen = cm->cmsg_len; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci } else if (version == AF_INET6) { 6898c2ecf20Sopenharmony_ci struct in6_pktinfo *pi6; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci cm->cmsg_level = SOL_IPV6; 6928c2ecf20Sopenharmony_ci cm->cmsg_type = IPV6_PKTINFO; 6938c2ecf20Sopenharmony_ci cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm)); 6968c2ecf20Sopenharmony_ci pi6->ipi6_ifindex = ifindex; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci m.msg_controllen = cm->cmsg_len; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ciagain: 7028c2ecf20Sopenharmony_ci err = sendmsg(sd, &m, 0); 7038c2ecf20Sopenharmony_ci if (err < 0) { 7048c2ecf20Sopenharmony_ci if (errno == EACCES && try_broadcast) { 7058c2ecf20Sopenharmony_ci try_broadcast = 0; 7068c2ecf20Sopenharmony_ci if (!set_broadcast(sd)) 7078c2ecf20Sopenharmony_ci goto again; 7088c2ecf20Sopenharmony_ci errno = EACCES; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci log_err_errno("sendmsg failed"); 7128c2ecf20Sopenharmony_ci return 1; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int send_msg(int sd, void *addr, socklen_t alen, struct sock_args *args) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci if (args->type == SOCK_STREAM) { 7228c2ecf20Sopenharmony_ci if (write(sd, msg, msglen) < 0) { 7238c2ecf20Sopenharmony_ci log_err_errno("write failed sending msg to peer"); 7248c2ecf20Sopenharmony_ci return 1; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci } else if (args->ifindex && args->use_cmsg) { 7278c2ecf20Sopenharmony_ci if (send_msg_cmsg(sd, addr, alen, args->ifindex, args->version)) 7288c2ecf20Sopenharmony_ci return 1; 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci if (send_msg_no_cmsg(sd, addr, alen)) 7318c2ecf20Sopenharmony_ci return 1; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci log_msg("Sent message:\n"); 7358c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", msg, msglen > 24 ? " ..." : ""); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int socket_read_dgram(int sd, struct sock_args *args) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci unsigned char addr[sizeof(struct sockaddr_in6)]; 7438c2ecf20Sopenharmony_ci struct sockaddr *sa = (struct sockaddr *) addr; 7448c2ecf20Sopenharmony_ci socklen_t alen = sizeof(addr); 7458c2ecf20Sopenharmony_ci struct iovec iov[2]; 7468c2ecf20Sopenharmony_ci struct msghdr m = { 7478c2ecf20Sopenharmony_ci .msg_name = (caddr_t)addr, 7488c2ecf20Sopenharmony_ci .msg_namelen = alen, 7498c2ecf20Sopenharmony_ci .msg_iov = iov, 7508c2ecf20Sopenharmony_ci .msg_iovlen = 1, 7518c2ecf20Sopenharmony_ci }; 7528c2ecf20Sopenharmony_ci unsigned char cmsgbuf[256]; 7538c2ecf20Sopenharmony_ci struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf; 7548c2ecf20Sopenharmony_ci char buf[16*1024]; 7558c2ecf20Sopenharmony_ci int ifindex; 7568c2ecf20Sopenharmony_ci int len; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci iov[0].iov_base = (caddr_t)buf; 7598c2ecf20Sopenharmony_ci iov[0].iov_len = sizeof(buf); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci memset(cmsgbuf, 0, sizeof(cmsgbuf)); 7628c2ecf20Sopenharmony_ci m.msg_control = (caddr_t)cm; 7638c2ecf20Sopenharmony_ci m.msg_controllen = sizeof(cmsgbuf); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci len = recvmsg(sd, &m, 0); 7668c2ecf20Sopenharmony_ci if (len == 0) { 7678c2ecf20Sopenharmony_ci log_msg("peer closed connection.\n"); 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci } else if (len < 0) { 7708c2ecf20Sopenharmony_ci log_msg("failed to read message: %d: %s\n", 7718c2ecf20Sopenharmony_ci errno, strerror(errno)); 7728c2ecf20Sopenharmony_ci return -1; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci buf[len] = '\0'; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci log_address("Message from:", sa); 7788c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : ""); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci ifindex = get_index_from_cmsg(&m); 7818c2ecf20Sopenharmony_ci if (args->expected_ifindex) { 7828c2ecf20Sopenharmony_ci if (args->expected_ifindex != ifindex) { 7838c2ecf20Sopenharmony_ci log_error("Device index mismatch: expected %d have %d\n", 7848c2ecf20Sopenharmony_ci args->expected_ifindex, ifindex); 7858c2ecf20Sopenharmony_ci return -1; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci log_msg("Device index matches: expected %d have %d\n", 7888c2ecf20Sopenharmony_ci args->expected_ifindex, ifindex); 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (!interactive && server_mode) { 7928c2ecf20Sopenharmony_ci if (sa->sa_family == AF_INET6) { 7938c2ecf20Sopenharmony_ci struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; 7948c2ecf20Sopenharmony_ci struct in6_addr *in6 = &s6->sin6_addr; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (IN6_IS_ADDR_V4MAPPED(in6)) { 7978c2ecf20Sopenharmony_ci const uint32_t *pa = (uint32_t *) &in6->s6_addr; 7988c2ecf20Sopenharmony_ci struct in_addr in4; 7998c2ecf20Sopenharmony_ci struct sockaddr_in *sin; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci sin = (struct sockaddr_in *) addr; 8028c2ecf20Sopenharmony_ci pa += 3; 8038c2ecf20Sopenharmony_ci in4.s_addr = *pa; 8048c2ecf20Sopenharmony_ci sin->sin_addr = in4; 8058c2ecf20Sopenharmony_ci sin->sin_family = AF_INET; 8068c2ecf20Sopenharmony_ci if (send_msg_cmsg(sd, addr, alen, 8078c2ecf20Sopenharmony_ci ifindex, AF_INET) < 0) 8088c2ecf20Sopenharmony_ci goto out_err; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ciagain: 8128c2ecf20Sopenharmony_ci iov[0].iov_len = len; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (args->version == AF_INET6) { 8158c2ecf20Sopenharmony_ci struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (args->dev) { 8188c2ecf20Sopenharmony_ci /* avoid PKTINFO conflicts with bindtodev */ 8198c2ecf20Sopenharmony_ci if (sendto(sd, buf, len, 0, 8208c2ecf20Sopenharmony_ci (void *) addr, alen) < 0) 8218c2ecf20Sopenharmony_ci goto out_err; 8228c2ecf20Sopenharmony_ci } else { 8238c2ecf20Sopenharmony_ci /* kernel is allowing scope_id to be set to VRF 8248c2ecf20Sopenharmony_ci * index for LLA. for sends to global address 8258c2ecf20Sopenharmony_ci * reset scope id 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci s6->sin6_scope_id = ifindex; 8288c2ecf20Sopenharmony_ci if (sendmsg(sd, &m, 0) < 0) 8298c2ecf20Sopenharmony_ci goto out_err; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci } else { 8328c2ecf20Sopenharmony_ci int err; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci err = sendmsg(sd, &m, 0); 8358c2ecf20Sopenharmony_ci if (err < 0) { 8368c2ecf20Sopenharmony_ci if (errno == EACCES && try_broadcast) { 8378c2ecf20Sopenharmony_ci try_broadcast = 0; 8388c2ecf20Sopenharmony_ci if (!set_broadcast(sd)) 8398c2ecf20Sopenharmony_ci goto again; 8408c2ecf20Sopenharmony_ci errno = EACCES; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci goto out_err; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci log_msg("Sent message:\n"); 8468c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : ""); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return 1; 8508c2ecf20Sopenharmony_ciout_err: 8518c2ecf20Sopenharmony_ci log_err_errno("failed to send msg to peer"); 8528c2ecf20Sopenharmony_ci return -1; 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic int socket_read_stream(int sd) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci char buf[1024]; 8588c2ecf20Sopenharmony_ci int len; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci len = read(sd, buf, sizeof(buf)-1); 8618c2ecf20Sopenharmony_ci if (len == 0) { 8628c2ecf20Sopenharmony_ci log_msg("client closed connection.\n"); 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci } else if (len < 0) { 8658c2ecf20Sopenharmony_ci log_msg("failed to read message\n"); 8668c2ecf20Sopenharmony_ci return -1; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci buf[len] = '\0'; 8708c2ecf20Sopenharmony_ci log_msg("Incoming message:\n"); 8718c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : ""); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (!interactive && server_mode) { 8748c2ecf20Sopenharmony_ci if (write(sd, buf, len) < 0) { 8758c2ecf20Sopenharmony_ci log_err_errno("failed to send buf"); 8768c2ecf20Sopenharmony_ci return -1; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci log_msg("Sent message:\n"); 8798c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : ""); 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return 1; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int socket_read(int sd, struct sock_args *args) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci if (args->type == SOCK_STREAM) 8888c2ecf20Sopenharmony_ci return socket_read_stream(sd); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return socket_read_dgram(sd, args); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int stdin_to_socket(int sd, int type, void *addr, socklen_t alen) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci char buf[1024]; 8968c2ecf20Sopenharmony_ci int len; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (fgets(buf, sizeof(buf), stdin) == NULL) 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci len = strlen(buf); 9028c2ecf20Sopenharmony_ci if (type == SOCK_STREAM) { 9038c2ecf20Sopenharmony_ci if (write(sd, buf, len) < 0) { 9048c2ecf20Sopenharmony_ci log_err_errno("failed to send buf"); 9058c2ecf20Sopenharmony_ci return -1; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } else { 9088c2ecf20Sopenharmony_ci int err; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ciagain: 9118c2ecf20Sopenharmony_ci err = sendto(sd, buf, len, 0, addr, alen); 9128c2ecf20Sopenharmony_ci if (err < 0) { 9138c2ecf20Sopenharmony_ci if (errno == EACCES && try_broadcast) { 9148c2ecf20Sopenharmony_ci try_broadcast = 0; 9158c2ecf20Sopenharmony_ci if (!set_broadcast(sd)) 9168c2ecf20Sopenharmony_ci goto again; 9178c2ecf20Sopenharmony_ci errno = EACCES; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci log_err_errno("failed to send msg to peer"); 9208c2ecf20Sopenharmony_ci return -1; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci log_msg("Sent message:\n"); 9248c2ecf20Sopenharmony_ci log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : ""); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci return 1; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic void set_recv_attr(int sd, int version) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci if (version == AF_INET6) { 9328c2ecf20Sopenharmony_ci set_recvpktinfo_v6(sd); 9338c2ecf20Sopenharmony_ci set_recverr_v6(sd); 9348c2ecf20Sopenharmony_ci } else { 9358c2ecf20Sopenharmony_ci set_pktinfo_v4(sd); 9368c2ecf20Sopenharmony_ci set_recverr_v4(sd); 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int msg_loop(int client, int sd, void *addr, socklen_t alen, 9418c2ecf20Sopenharmony_ci struct sock_args *args) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL; 9448c2ecf20Sopenharmony_ci fd_set rfds; 9458c2ecf20Sopenharmony_ci int nfds; 9468c2ecf20Sopenharmony_ci int rc; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (args->type != SOCK_STREAM) 9498c2ecf20Sopenharmony_ci set_recv_attr(sd, args->version); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (msg) { 9528c2ecf20Sopenharmony_ci msglen = strlen(msg); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* client sends first message */ 9558c2ecf20Sopenharmony_ci if (client) { 9568c2ecf20Sopenharmony_ci if (send_msg(sd, addr, alen, args)) 9578c2ecf20Sopenharmony_ci return 1; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci if (!interactive) { 9608c2ecf20Sopenharmony_ci ptval = &timeout; 9618c2ecf20Sopenharmony_ci if (!prog_timeout) 9628c2ecf20Sopenharmony_ci timeout.tv_sec = 5; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci nfds = interactive ? MAX(fileno(stdin), sd) + 1 : sd + 1; 9678c2ecf20Sopenharmony_ci while (1) { 9688c2ecf20Sopenharmony_ci FD_ZERO(&rfds); 9698c2ecf20Sopenharmony_ci FD_SET(sd, &rfds); 9708c2ecf20Sopenharmony_ci if (interactive) 9718c2ecf20Sopenharmony_ci FD_SET(fileno(stdin), &rfds); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci rc = select(nfds, &rfds, NULL, NULL, ptval); 9748c2ecf20Sopenharmony_ci if (rc < 0) { 9758c2ecf20Sopenharmony_ci if (errno == EINTR) 9768c2ecf20Sopenharmony_ci continue; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci rc = 1; 9798c2ecf20Sopenharmony_ci log_err_errno("select failed"); 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci } else if (rc == 0) { 9828c2ecf20Sopenharmony_ci log_error("Timed out waiting for response\n"); 9838c2ecf20Sopenharmony_ci rc = 2; 9848c2ecf20Sopenharmony_ci break; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (FD_ISSET(sd, &rfds)) { 9888c2ecf20Sopenharmony_ci rc = socket_read(sd, args); 9898c2ecf20Sopenharmony_ci if (rc < 0) { 9908c2ecf20Sopenharmony_ci rc = 1; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci if (rc == 0) 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci rc = 0; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (FD_ISSET(fileno(stdin), &rfds)) { 10008c2ecf20Sopenharmony_ci if (stdin_to_socket(sd, args->type, addr, alen) <= 0) 10018c2ecf20Sopenharmony_ci break; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (interactive) 10058c2ecf20Sopenharmony_ci continue; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (iter != -1) { 10088c2ecf20Sopenharmony_ci --iter; 10098c2ecf20Sopenharmony_ci if (iter == 0) 10108c2ecf20Sopenharmony_ci break; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci log_msg("Going into quiet mode\n"); 10148c2ecf20Sopenharmony_ci quiet = 1; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (client) { 10178c2ecf20Sopenharmony_ci if (send_msg(sd, addr, alen, args)) { 10188c2ecf20Sopenharmony_ci rc = 1; 10198c2ecf20Sopenharmony_ci break; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci return rc; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int msock_init(struct sock_args *args, int server) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci uint32_t if_addr = htonl(INADDR_ANY); 10308c2ecf20Sopenharmony_ci struct sockaddr_in laddr = { 10318c2ecf20Sopenharmony_ci .sin_family = AF_INET, 10328c2ecf20Sopenharmony_ci .sin_port = htons(args->port), 10338c2ecf20Sopenharmony_ci }; 10348c2ecf20Sopenharmony_ci int one = 1; 10358c2ecf20Sopenharmony_ci int sd; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (!server && args->has_local_ip) 10388c2ecf20Sopenharmony_ci if_addr = args->local_addr.in.s_addr; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci sd = socket(PF_INET, SOCK_DGRAM, 0); 10418c2ecf20Sopenharmony_ci if (sd < 0) { 10428c2ecf20Sopenharmony_ci log_err_errno("socket"); 10438c2ecf20Sopenharmony_ci return -1; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, 10478c2ecf20Sopenharmony_ci (char *)&one, sizeof(one)) < 0) { 10488c2ecf20Sopenharmony_ci log_err_errno("Setting SO_REUSEADDR error"); 10498c2ecf20Sopenharmony_ci goto out_err; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, 10538c2ecf20Sopenharmony_ci (char *)&one, sizeof(one)) < 0) 10548c2ecf20Sopenharmony_ci log_err_errno("Setting SO_BROADCAST error"); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (args->dev && bind_to_device(sd, args->dev) != 0) 10578c2ecf20Sopenharmony_ci goto out_err; 10588c2ecf20Sopenharmony_ci else if (args->use_setsockopt && 10598c2ecf20Sopenharmony_ci set_multicast_if(sd, args->ifindex)) 10608c2ecf20Sopenharmony_ci goto out_err; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci laddr.sin_addr.s_addr = if_addr; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (bind(sd, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) { 10658c2ecf20Sopenharmony_ci log_err_errno("bind failed"); 10668c2ecf20Sopenharmony_ci goto out_err; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (server && 10708c2ecf20Sopenharmony_ci set_membership(sd, args->grp.s_addr, 10718c2ecf20Sopenharmony_ci args->local_addr.in.s_addr, args->ifindex)) 10728c2ecf20Sopenharmony_ci goto out_err; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci return sd; 10758c2ecf20Sopenharmony_ciout_err: 10768c2ecf20Sopenharmony_ci close(sd); 10778c2ecf20Sopenharmony_ci return -1; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic int msock_server(struct sock_args *args) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci return msock_init(args, 1); 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic int msock_client(struct sock_args *args) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci return msock_init(args, 0); 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic int bind_socket(int sd, struct sock_args *args) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci struct sockaddr_in serv_addr = { 10938c2ecf20Sopenharmony_ci .sin_family = AF_INET, 10948c2ecf20Sopenharmony_ci }; 10958c2ecf20Sopenharmony_ci struct sockaddr_in6 serv6_addr = { 10968c2ecf20Sopenharmony_ci .sin6_family = AF_INET6, 10978c2ecf20Sopenharmony_ci }; 10988c2ecf20Sopenharmony_ci void *addr; 10998c2ecf20Sopenharmony_ci socklen_t alen; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (!args->has_local_ip && args->type == SOCK_RAW) 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci switch (args->version) { 11058c2ecf20Sopenharmony_ci case AF_INET: 11068c2ecf20Sopenharmony_ci serv_addr.sin_port = htons(args->port); 11078c2ecf20Sopenharmony_ci serv_addr.sin_addr = args->local_addr.in; 11088c2ecf20Sopenharmony_ci addr = &serv_addr; 11098c2ecf20Sopenharmony_ci alen = sizeof(serv_addr); 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci case AF_INET6: 11138c2ecf20Sopenharmony_ci serv6_addr.sin6_port = htons(args->port); 11148c2ecf20Sopenharmony_ci serv6_addr.sin6_addr = args->local_addr.in6; 11158c2ecf20Sopenharmony_ci addr = &serv6_addr; 11168c2ecf20Sopenharmony_ci alen = sizeof(serv6_addr); 11178c2ecf20Sopenharmony_ci break; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci default: 11208c2ecf20Sopenharmony_ci log_error("Invalid address family\n"); 11218c2ecf20Sopenharmony_ci return -1; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (bind(sd, addr, alen) < 0) { 11258c2ecf20Sopenharmony_ci log_err_errno("error binding socket"); 11268c2ecf20Sopenharmony_ci return -1; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return 0; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int lsock_init(struct sock_args *args) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci long flags; 11358c2ecf20Sopenharmony_ci int sd; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci sd = socket(args->version, args->type, args->protocol); 11388c2ecf20Sopenharmony_ci if (sd < 0) { 11398c2ecf20Sopenharmony_ci log_err_errno("Error opening socket"); 11408c2ecf20Sopenharmony_ci return -1; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (set_reuseaddr(sd) != 0) 11448c2ecf20Sopenharmony_ci goto err; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (set_reuseport(sd) != 0) 11478c2ecf20Sopenharmony_ci goto err; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (args->dev && bind_to_device(sd, args->dev) != 0) 11508c2ecf20Sopenharmony_ci goto err; 11518c2ecf20Sopenharmony_ci else if (args->use_setsockopt && 11528c2ecf20Sopenharmony_ci set_unicast_if(sd, args->ifindex, args->version)) 11538c2ecf20Sopenharmony_ci goto err; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (bind_socket(sd, args)) 11568c2ecf20Sopenharmony_ci goto err; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (args->bind_test_only) 11598c2ecf20Sopenharmony_ci goto out; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (args->type == SOCK_STREAM && listen(sd, 1) < 0) { 11628c2ecf20Sopenharmony_ci log_err_errno("listen failed"); 11638c2ecf20Sopenharmony_ci goto err; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci flags = fcntl(sd, F_GETFL); 11678c2ecf20Sopenharmony_ci if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) { 11688c2ecf20Sopenharmony_ci log_err_errno("Failed to set non-blocking option"); 11698c2ecf20Sopenharmony_ci goto err; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (fcntl(sd, F_SETFD, FD_CLOEXEC) < 0) 11738c2ecf20Sopenharmony_ci log_err_errno("Failed to set close-on-exec flag"); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ciout: 11768c2ecf20Sopenharmony_ci return sd; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cierr: 11798c2ecf20Sopenharmony_ci close(sd); 11808c2ecf20Sopenharmony_ci return -1; 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic int do_server(struct sock_args *args) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL; 11868c2ecf20Sopenharmony_ci unsigned char addr[sizeof(struct sockaddr_in6)] = {}; 11878c2ecf20Sopenharmony_ci socklen_t alen = sizeof(addr); 11888c2ecf20Sopenharmony_ci int lsd, csd = -1; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci fd_set rfds; 11918c2ecf20Sopenharmony_ci int rc; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (prog_timeout) 11948c2ecf20Sopenharmony_ci ptval = &timeout; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (args->has_grp) 11978c2ecf20Sopenharmony_ci lsd = msock_server(args); 11988c2ecf20Sopenharmony_ci else 11998c2ecf20Sopenharmony_ci lsd = lsock_init(args); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (lsd < 0) 12028c2ecf20Sopenharmony_ci return 1; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (args->bind_test_only) { 12058c2ecf20Sopenharmony_ci close(lsd); 12068c2ecf20Sopenharmony_ci return 0; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (args->type != SOCK_STREAM) { 12108c2ecf20Sopenharmony_ci rc = msg_loop(0, lsd, (void *) addr, alen, args); 12118c2ecf20Sopenharmony_ci close(lsd); 12128c2ecf20Sopenharmony_ci return rc; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (args->password && tcp_md5_remote(lsd, args)) { 12168c2ecf20Sopenharmony_ci close(lsd); 12178c2ecf20Sopenharmony_ci return 1; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci while (1) { 12218c2ecf20Sopenharmony_ci log_msg("\n"); 12228c2ecf20Sopenharmony_ci log_msg("waiting for client connection.\n"); 12238c2ecf20Sopenharmony_ci FD_ZERO(&rfds); 12248c2ecf20Sopenharmony_ci FD_SET(lsd, &rfds); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci rc = select(lsd+1, &rfds, NULL, NULL, ptval); 12278c2ecf20Sopenharmony_ci if (rc == 0) { 12288c2ecf20Sopenharmony_ci rc = 2; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (rc < 0) { 12338c2ecf20Sopenharmony_ci if (errno == EINTR) 12348c2ecf20Sopenharmony_ci continue; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci log_err_errno("select failed"); 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (FD_ISSET(lsd, &rfds)) { 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci csd = accept(lsd, (void *) addr, &alen); 12438c2ecf20Sopenharmony_ci if (csd < 0) { 12448c2ecf20Sopenharmony_ci log_err_errno("accept failed"); 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci rc = show_sockstat(csd, args); 12498c2ecf20Sopenharmony_ci if (rc) 12508c2ecf20Sopenharmony_ci break; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci rc = check_device(csd, args); 12538c2ecf20Sopenharmony_ci if (rc) 12548c2ecf20Sopenharmony_ci break; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci rc = msg_loop(0, csd, (void *) addr, alen, args); 12588c2ecf20Sopenharmony_ci close(csd); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci if (!interactive) 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci close(lsd); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci return rc; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int wait_for_connect(int sd) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci struct timeval _tv = { .tv_sec = prog_timeout }, *tv = NULL; 12728c2ecf20Sopenharmony_ci fd_set wfd; 12738c2ecf20Sopenharmony_ci int val = 0, sz = sizeof(val); 12748c2ecf20Sopenharmony_ci int rc; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci FD_ZERO(&wfd); 12778c2ecf20Sopenharmony_ci FD_SET(sd, &wfd); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (prog_timeout) 12808c2ecf20Sopenharmony_ci tv = &_tv; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci rc = select(FD_SETSIZE, NULL, &wfd, NULL, tv); 12838c2ecf20Sopenharmony_ci if (rc == 0) { 12848c2ecf20Sopenharmony_ci log_error("connect timed out\n"); 12858c2ecf20Sopenharmony_ci return -2; 12868c2ecf20Sopenharmony_ci } else if (rc < 0) { 12878c2ecf20Sopenharmony_ci log_err_errno("select failed"); 12888c2ecf20Sopenharmony_ci return -3; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &val, (socklen_t *)&sz) < 0) { 12928c2ecf20Sopenharmony_ci log_err_errno("getsockopt(SO_ERROR) failed"); 12938c2ecf20Sopenharmony_ci return -4; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci if (val != 0) { 12978c2ecf20Sopenharmony_ci log_error("connect failed: %d: %s\n", val, strerror(val)); 12988c2ecf20Sopenharmony_ci return -1; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return 0; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic int connectsock(void *addr, socklen_t alen, struct sock_args *args) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci int sd, rc = -1; 13078c2ecf20Sopenharmony_ci long flags; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci sd = socket(args->version, args->type, args->protocol); 13108c2ecf20Sopenharmony_ci if (sd < 0) { 13118c2ecf20Sopenharmony_ci log_err_errno("Failed to create socket"); 13128c2ecf20Sopenharmony_ci return -1; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci flags = fcntl(sd, F_GETFL); 13168c2ecf20Sopenharmony_ci if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) { 13178c2ecf20Sopenharmony_ci log_err_errno("Failed to set non-blocking option"); 13188c2ecf20Sopenharmony_ci goto err; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (set_reuseport(sd) != 0) 13228c2ecf20Sopenharmony_ci goto err; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci if (args->dev && bind_to_device(sd, args->dev) != 0) 13258c2ecf20Sopenharmony_ci goto err; 13268c2ecf20Sopenharmony_ci else if (args->use_setsockopt && 13278c2ecf20Sopenharmony_ci set_unicast_if(sd, args->ifindex, args->version)) 13288c2ecf20Sopenharmony_ci goto err; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (args->has_local_ip && bind_socket(sd, args)) 13318c2ecf20Sopenharmony_ci goto err; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (args->type != SOCK_STREAM) 13348c2ecf20Sopenharmony_ci goto out; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (args->password && tcp_md5sig(sd, addr, alen, args)) 13378c2ecf20Sopenharmony_ci goto err; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (args->bind_test_only) 13408c2ecf20Sopenharmony_ci goto out; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (connect(sd, addr, alen) < 0) { 13438c2ecf20Sopenharmony_ci if (errno != EINPROGRESS) { 13448c2ecf20Sopenharmony_ci log_err_errno("Failed to connect to remote host"); 13458c2ecf20Sopenharmony_ci rc = -1; 13468c2ecf20Sopenharmony_ci goto err; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci rc = wait_for_connect(sd); 13498c2ecf20Sopenharmony_ci if (rc < 0) 13508c2ecf20Sopenharmony_ci goto err; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ciout: 13538c2ecf20Sopenharmony_ci return sd; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cierr: 13568c2ecf20Sopenharmony_ci close(sd); 13578c2ecf20Sopenharmony_ci return rc; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic int do_client(struct sock_args *args) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci struct sockaddr_in sin = { 13638c2ecf20Sopenharmony_ci .sin_family = AF_INET, 13648c2ecf20Sopenharmony_ci }; 13658c2ecf20Sopenharmony_ci struct sockaddr_in6 sin6 = { 13668c2ecf20Sopenharmony_ci .sin6_family = AF_INET6, 13678c2ecf20Sopenharmony_ci }; 13688c2ecf20Sopenharmony_ci void *addr; 13698c2ecf20Sopenharmony_ci int alen; 13708c2ecf20Sopenharmony_ci int rc = 0; 13718c2ecf20Sopenharmony_ci int sd; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (!args->has_remote_ip && !args->has_grp) { 13748c2ecf20Sopenharmony_ci fprintf(stderr, "remote IP or multicast group not given\n"); 13758c2ecf20Sopenharmony_ci return 1; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci switch (args->version) { 13798c2ecf20Sopenharmony_ci case AF_INET: 13808c2ecf20Sopenharmony_ci sin.sin_port = htons(args->port); 13818c2ecf20Sopenharmony_ci if (args->has_grp) 13828c2ecf20Sopenharmony_ci sin.sin_addr = args->grp; 13838c2ecf20Sopenharmony_ci else 13848c2ecf20Sopenharmony_ci sin.sin_addr = args->remote_addr.in; 13858c2ecf20Sopenharmony_ci addr = &sin; 13868c2ecf20Sopenharmony_ci alen = sizeof(sin); 13878c2ecf20Sopenharmony_ci break; 13888c2ecf20Sopenharmony_ci case AF_INET6: 13898c2ecf20Sopenharmony_ci sin6.sin6_port = htons(args->port); 13908c2ecf20Sopenharmony_ci sin6.sin6_addr = args->remote_addr.in6; 13918c2ecf20Sopenharmony_ci sin6.sin6_scope_id = args->scope_id; 13928c2ecf20Sopenharmony_ci addr = &sin6; 13938c2ecf20Sopenharmony_ci alen = sizeof(sin6); 13948c2ecf20Sopenharmony_ci break; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (args->has_grp) 13988c2ecf20Sopenharmony_ci sd = msock_client(args); 13998c2ecf20Sopenharmony_ci else 14008c2ecf20Sopenharmony_ci sd = connectsock(addr, alen, args); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (sd < 0) 14038c2ecf20Sopenharmony_ci return -sd; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci if (args->bind_test_only) 14068c2ecf20Sopenharmony_ci goto out; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (args->type == SOCK_STREAM) { 14098c2ecf20Sopenharmony_ci rc = show_sockstat(sd, args); 14108c2ecf20Sopenharmony_ci if (rc != 0) 14118c2ecf20Sopenharmony_ci goto out; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci rc = msg_loop(1, sd, addr, alen, args); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ciout: 14178c2ecf20Sopenharmony_ci close(sd); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci return rc; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cienum addr_type { 14238c2ecf20Sopenharmony_ci ADDR_TYPE_LOCAL, 14248c2ecf20Sopenharmony_ci ADDR_TYPE_REMOTE, 14258c2ecf20Sopenharmony_ci ADDR_TYPE_MCAST, 14268c2ecf20Sopenharmony_ci ADDR_TYPE_EXPECTED_LOCAL, 14278c2ecf20Sopenharmony_ci ADDR_TYPE_EXPECTED_REMOTE, 14288c2ecf20Sopenharmony_ci ADDR_TYPE_MD5_PREFIX, 14298c2ecf20Sopenharmony_ci}; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_cistatic int convert_addr(struct sock_args *args, const char *_str, 14328c2ecf20Sopenharmony_ci enum addr_type atype) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci int pfx_len_max = args->version == AF_INET6 ? 128 : 32; 14358c2ecf20Sopenharmony_ci int family = args->version; 14368c2ecf20Sopenharmony_ci char *str, *dev, *sep; 14378c2ecf20Sopenharmony_ci struct in6_addr *in6; 14388c2ecf20Sopenharmony_ci struct in_addr *in; 14398c2ecf20Sopenharmony_ci const char *desc; 14408c2ecf20Sopenharmony_ci void *addr; 14418c2ecf20Sopenharmony_ci int rc = 0; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci str = strdup(_str); 14448c2ecf20Sopenharmony_ci if (!str) 14458c2ecf20Sopenharmony_ci return -ENOMEM; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci switch (atype) { 14488c2ecf20Sopenharmony_ci case ADDR_TYPE_LOCAL: 14498c2ecf20Sopenharmony_ci desc = "local"; 14508c2ecf20Sopenharmony_ci addr = &args->local_addr; 14518c2ecf20Sopenharmony_ci break; 14528c2ecf20Sopenharmony_ci case ADDR_TYPE_REMOTE: 14538c2ecf20Sopenharmony_ci desc = "remote"; 14548c2ecf20Sopenharmony_ci addr = &args->remote_addr; 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci case ADDR_TYPE_MCAST: 14578c2ecf20Sopenharmony_ci desc = "mcast grp"; 14588c2ecf20Sopenharmony_ci addr = &args->grp; 14598c2ecf20Sopenharmony_ci break; 14608c2ecf20Sopenharmony_ci case ADDR_TYPE_EXPECTED_LOCAL: 14618c2ecf20Sopenharmony_ci desc = "expected local"; 14628c2ecf20Sopenharmony_ci addr = &args->expected_laddr; 14638c2ecf20Sopenharmony_ci break; 14648c2ecf20Sopenharmony_ci case ADDR_TYPE_EXPECTED_REMOTE: 14658c2ecf20Sopenharmony_ci desc = "expected remote"; 14668c2ecf20Sopenharmony_ci addr = &args->expected_raddr; 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci case ADDR_TYPE_MD5_PREFIX: 14698c2ecf20Sopenharmony_ci desc = "md5 prefix"; 14708c2ecf20Sopenharmony_ci if (family == AF_INET) { 14718c2ecf20Sopenharmony_ci args->md5_prefix.v4.sin_family = AF_INET; 14728c2ecf20Sopenharmony_ci addr = &args->md5_prefix.v4.sin_addr; 14738c2ecf20Sopenharmony_ci } else if (family == AF_INET6) { 14748c2ecf20Sopenharmony_ci args->md5_prefix.v6.sin6_family = AF_INET6; 14758c2ecf20Sopenharmony_ci addr = &args->md5_prefix.v6.sin6_addr; 14768c2ecf20Sopenharmony_ci } else 14778c2ecf20Sopenharmony_ci return 1; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci sep = strchr(str, '/'); 14808c2ecf20Sopenharmony_ci if (sep) { 14818c2ecf20Sopenharmony_ci *sep = '\0'; 14828c2ecf20Sopenharmony_ci sep++; 14838c2ecf20Sopenharmony_ci if (str_to_uint(sep, 1, pfx_len_max, 14848c2ecf20Sopenharmony_ci &args->prefix_len) != 0) { 14858c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid port\n"); 14868c2ecf20Sopenharmony_ci return 1; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci } else { 14898c2ecf20Sopenharmony_ci args->prefix_len = pfx_len_max; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci break; 14928c2ecf20Sopenharmony_ci default: 14938c2ecf20Sopenharmony_ci log_error("unknown address type"); 14948c2ecf20Sopenharmony_ci exit(1); 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci switch (family) { 14988c2ecf20Sopenharmony_ci case AF_INET: 14998c2ecf20Sopenharmony_ci in = (struct in_addr *) addr; 15008c2ecf20Sopenharmony_ci if (str) { 15018c2ecf20Sopenharmony_ci if (inet_pton(AF_INET, str, in) == 0) { 15028c2ecf20Sopenharmony_ci log_error("Invalid %s IP address\n", desc); 15038c2ecf20Sopenharmony_ci rc = -1; 15048c2ecf20Sopenharmony_ci goto out; 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci } else { 15078c2ecf20Sopenharmony_ci in->s_addr = htonl(INADDR_ANY); 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci break; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci case AF_INET6: 15128c2ecf20Sopenharmony_ci dev = strchr(str, '%'); 15138c2ecf20Sopenharmony_ci if (dev) { 15148c2ecf20Sopenharmony_ci *dev = '\0'; 15158c2ecf20Sopenharmony_ci dev++; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci in6 = (struct in6_addr *) addr; 15198c2ecf20Sopenharmony_ci if (str) { 15208c2ecf20Sopenharmony_ci if (inet_pton(AF_INET6, str, in6) == 0) { 15218c2ecf20Sopenharmony_ci log_error("Invalid %s IPv6 address\n", desc); 15228c2ecf20Sopenharmony_ci rc = -1; 15238c2ecf20Sopenharmony_ci goto out; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci } else { 15268c2ecf20Sopenharmony_ci *in6 = in6addr_any; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci if (dev) { 15298c2ecf20Sopenharmony_ci args->scope_id = get_ifidx(dev); 15308c2ecf20Sopenharmony_ci if (args->scope_id < 0) { 15318c2ecf20Sopenharmony_ci log_error("Invalid scope on %s IPv6 address\n", 15328c2ecf20Sopenharmony_ci desc); 15338c2ecf20Sopenharmony_ci rc = -1; 15348c2ecf20Sopenharmony_ci goto out; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci break; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci default: 15408c2ecf20Sopenharmony_ci log_error("Invalid address family\n"); 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ciout: 15448c2ecf20Sopenharmony_ci free(str); 15458c2ecf20Sopenharmony_ci return rc; 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic char *random_msg(int len) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci int i, n = 0, olen = len + 1; 15518c2ecf20Sopenharmony_ci char *m; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (len <= 0) 15548c2ecf20Sopenharmony_ci return NULL; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci m = malloc(olen); 15578c2ecf20Sopenharmony_ci if (!m) 15588c2ecf20Sopenharmony_ci return NULL; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci while (len > 26) { 15618c2ecf20Sopenharmony_ci i = snprintf(m + n, olen - n, "%.26s", 15628c2ecf20Sopenharmony_ci "abcdefghijklmnopqrstuvwxyz"); 15638c2ecf20Sopenharmony_ci n += i; 15648c2ecf20Sopenharmony_ci len -= i; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci i = snprintf(m + n, olen - n, "%.*s", len, 15678c2ecf20Sopenharmony_ci "abcdefghijklmnopqrstuvwxyz"); 15688c2ecf20Sopenharmony_ci return m; 15698c2ecf20Sopenharmony_ci} 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci#define GETOPT_STR "sr:l:p:t:g:P:DRn:M:m:d:SCi6L:0:1:2:Fbq" 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic void print_usage(char *prog) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci printf( 15768c2ecf20Sopenharmony_ci "usage: %s OPTS\n" 15778c2ecf20Sopenharmony_ci "Required:\n" 15788c2ecf20Sopenharmony_ci " -r addr remote address to connect to (client mode only)\n" 15798c2ecf20Sopenharmony_ci " -p port port to connect to (client mode)/listen on (server mode)\n" 15808c2ecf20Sopenharmony_ci " (default: %d)\n" 15818c2ecf20Sopenharmony_ci " -s server mode (default: client mode)\n" 15828c2ecf20Sopenharmony_ci " -t timeout seconds (default: none)\n" 15838c2ecf20Sopenharmony_ci "\n" 15848c2ecf20Sopenharmony_ci "Optional:\n" 15858c2ecf20Sopenharmony_ci " -F Restart server loop\n" 15868c2ecf20Sopenharmony_ci " -6 IPv6 (default is IPv4)\n" 15878c2ecf20Sopenharmony_ci " -P proto protocol for socket: icmp, ospf (default: none)\n" 15888c2ecf20Sopenharmony_ci " -D|R datagram (D) / raw (R) socket (default stream)\n" 15898c2ecf20Sopenharmony_ci " -l addr local address to bind to\n" 15908c2ecf20Sopenharmony_ci "\n" 15918c2ecf20Sopenharmony_ci " -d dev bind socket to given device name\n" 15928c2ecf20Sopenharmony_ci " -S use setsockopt (IP_UNICAST_IF or IP_MULTICAST_IF)\n" 15938c2ecf20Sopenharmony_ci " to set device binding\n" 15948c2ecf20Sopenharmony_ci " -C use cmsg and IP_PKTINFO to specify device binding\n" 15958c2ecf20Sopenharmony_ci "\n" 15968c2ecf20Sopenharmony_ci " -L len send random message of given length\n" 15978c2ecf20Sopenharmony_ci " -n num number of times to send message\n" 15988c2ecf20Sopenharmony_ci "\n" 15998c2ecf20Sopenharmony_ci " -M password use MD5 sum protection\n" 16008c2ecf20Sopenharmony_ci " -m prefix/len prefix and length to use for MD5 key\n" 16018c2ecf20Sopenharmony_ci " -g grp multicast group (e.g., 239.1.1.1)\n" 16028c2ecf20Sopenharmony_ci " -i interactive mode (default is echo and terminate)\n" 16038c2ecf20Sopenharmony_ci "\n" 16048c2ecf20Sopenharmony_ci " -0 addr Expected local address\n" 16058c2ecf20Sopenharmony_ci " -1 addr Expected remote address\n" 16068c2ecf20Sopenharmony_ci " -2 dev Expected device name (or index) to receive packet\n" 16078c2ecf20Sopenharmony_ci "\n" 16088c2ecf20Sopenharmony_ci " -b Bind test only.\n" 16098c2ecf20Sopenharmony_ci " -q Be quiet. Run test without printing anything.\n" 16108c2ecf20Sopenharmony_ci , prog, DEFAULT_PORT); 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci struct sock_args args = { 16168c2ecf20Sopenharmony_ci .version = AF_INET, 16178c2ecf20Sopenharmony_ci .type = SOCK_STREAM, 16188c2ecf20Sopenharmony_ci .port = DEFAULT_PORT, 16198c2ecf20Sopenharmony_ci }; 16208c2ecf20Sopenharmony_ci struct protoent *pe; 16218c2ecf20Sopenharmony_ci unsigned int tmp; 16228c2ecf20Sopenharmony_ci int forever = 0; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* process inputs */ 16258c2ecf20Sopenharmony_ci extern char *optarg; 16268c2ecf20Sopenharmony_ci int rc = 0; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci /* 16298c2ecf20Sopenharmony_ci * process input args 16308c2ecf20Sopenharmony_ci */ 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci while ((rc = getopt(argc, argv, GETOPT_STR)) != -1) { 16338c2ecf20Sopenharmony_ci switch (rc) { 16348c2ecf20Sopenharmony_ci case 's': 16358c2ecf20Sopenharmony_ci server_mode = 1; 16368c2ecf20Sopenharmony_ci break; 16378c2ecf20Sopenharmony_ci case 'F': 16388c2ecf20Sopenharmony_ci forever = 1; 16398c2ecf20Sopenharmony_ci break; 16408c2ecf20Sopenharmony_ci case 'l': 16418c2ecf20Sopenharmony_ci args.has_local_ip = 1; 16428c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, ADDR_TYPE_LOCAL) < 0) 16438c2ecf20Sopenharmony_ci return 1; 16448c2ecf20Sopenharmony_ci break; 16458c2ecf20Sopenharmony_ci case 'r': 16468c2ecf20Sopenharmony_ci args.has_remote_ip = 1; 16478c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, ADDR_TYPE_REMOTE) < 0) 16488c2ecf20Sopenharmony_ci return 1; 16498c2ecf20Sopenharmony_ci break; 16508c2ecf20Sopenharmony_ci case 'p': 16518c2ecf20Sopenharmony_ci if (str_to_uint(optarg, 1, 65535, &tmp) != 0) { 16528c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid port\n"); 16538c2ecf20Sopenharmony_ci return 1; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci args.port = (unsigned short) tmp; 16568c2ecf20Sopenharmony_ci break; 16578c2ecf20Sopenharmony_ci case 't': 16588c2ecf20Sopenharmony_ci if (str_to_uint(optarg, 0, INT_MAX, 16598c2ecf20Sopenharmony_ci &prog_timeout) != 0) { 16608c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid timeout\n"); 16618c2ecf20Sopenharmony_ci return 1; 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci break; 16648c2ecf20Sopenharmony_ci case 'D': 16658c2ecf20Sopenharmony_ci args.type = SOCK_DGRAM; 16668c2ecf20Sopenharmony_ci break; 16678c2ecf20Sopenharmony_ci case 'R': 16688c2ecf20Sopenharmony_ci args.type = SOCK_RAW; 16698c2ecf20Sopenharmony_ci args.port = 0; 16708c2ecf20Sopenharmony_ci if (!args.protocol) 16718c2ecf20Sopenharmony_ci args.protocol = IPPROTO_RAW; 16728c2ecf20Sopenharmony_ci break; 16738c2ecf20Sopenharmony_ci case 'P': 16748c2ecf20Sopenharmony_ci pe = getprotobyname(optarg); 16758c2ecf20Sopenharmony_ci if (pe) { 16768c2ecf20Sopenharmony_ci args.protocol = pe->p_proto; 16778c2ecf20Sopenharmony_ci } else { 16788c2ecf20Sopenharmony_ci if (str_to_uint(optarg, 0, 0xffff, &tmp) != 0) { 16798c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid protocol\n"); 16808c2ecf20Sopenharmony_ci return 1; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci args.protocol = tmp; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci case 'n': 16868c2ecf20Sopenharmony_ci iter = atoi(optarg); 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci case 'L': 16898c2ecf20Sopenharmony_ci msg = random_msg(atoi(optarg)); 16908c2ecf20Sopenharmony_ci break; 16918c2ecf20Sopenharmony_ci case 'M': 16928c2ecf20Sopenharmony_ci args.password = optarg; 16938c2ecf20Sopenharmony_ci break; 16948c2ecf20Sopenharmony_ci case 'm': 16958c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, ADDR_TYPE_MD5_PREFIX) < 0) 16968c2ecf20Sopenharmony_ci return 1; 16978c2ecf20Sopenharmony_ci break; 16988c2ecf20Sopenharmony_ci case 'S': 16998c2ecf20Sopenharmony_ci args.use_setsockopt = 1; 17008c2ecf20Sopenharmony_ci break; 17018c2ecf20Sopenharmony_ci case 'C': 17028c2ecf20Sopenharmony_ci args.use_cmsg = 1; 17038c2ecf20Sopenharmony_ci break; 17048c2ecf20Sopenharmony_ci case 'd': 17058c2ecf20Sopenharmony_ci args.dev = optarg; 17068c2ecf20Sopenharmony_ci args.ifindex = get_ifidx(optarg); 17078c2ecf20Sopenharmony_ci if (args.ifindex < 0) { 17088c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid device name\n"); 17098c2ecf20Sopenharmony_ci return 1; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci case 'i': 17138c2ecf20Sopenharmony_ci interactive = 1; 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci case 'g': 17168c2ecf20Sopenharmony_ci args.has_grp = 1; 17178c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, ADDR_TYPE_MCAST) < 0) 17188c2ecf20Sopenharmony_ci return 1; 17198c2ecf20Sopenharmony_ci args.type = SOCK_DGRAM; 17208c2ecf20Sopenharmony_ci break; 17218c2ecf20Sopenharmony_ci case '6': 17228c2ecf20Sopenharmony_ci args.version = AF_INET6; 17238c2ecf20Sopenharmony_ci break; 17248c2ecf20Sopenharmony_ci case 'b': 17258c2ecf20Sopenharmony_ci args.bind_test_only = 1; 17268c2ecf20Sopenharmony_ci break; 17278c2ecf20Sopenharmony_ci case '0': 17288c2ecf20Sopenharmony_ci args.has_expected_laddr = 1; 17298c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, 17308c2ecf20Sopenharmony_ci ADDR_TYPE_EXPECTED_LOCAL)) 17318c2ecf20Sopenharmony_ci return 1; 17328c2ecf20Sopenharmony_ci break; 17338c2ecf20Sopenharmony_ci case '1': 17348c2ecf20Sopenharmony_ci args.has_expected_raddr = 1; 17358c2ecf20Sopenharmony_ci if (convert_addr(&args, optarg, 17368c2ecf20Sopenharmony_ci ADDR_TYPE_EXPECTED_REMOTE)) 17378c2ecf20Sopenharmony_ci return 1; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci break; 17408c2ecf20Sopenharmony_ci case '2': 17418c2ecf20Sopenharmony_ci if (str_to_uint(optarg, 0, INT_MAX, &tmp) == 0) { 17428c2ecf20Sopenharmony_ci args.expected_ifindex = (int)tmp; 17438c2ecf20Sopenharmony_ci } else { 17448c2ecf20Sopenharmony_ci args.expected_ifindex = get_ifidx(optarg); 17458c2ecf20Sopenharmony_ci if (args.expected_ifindex < 0) { 17468c2ecf20Sopenharmony_ci fprintf(stderr, 17478c2ecf20Sopenharmony_ci "Invalid expected device\n"); 17488c2ecf20Sopenharmony_ci return 1; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci break; 17528c2ecf20Sopenharmony_ci case 'q': 17538c2ecf20Sopenharmony_ci quiet = 1; 17548c2ecf20Sopenharmony_ci break; 17558c2ecf20Sopenharmony_ci default: 17568c2ecf20Sopenharmony_ci print_usage(argv[0]); 17578c2ecf20Sopenharmony_ci return 1; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (args.password && 17628c2ecf20Sopenharmony_ci ((!args.has_remote_ip && !args.prefix_len) || args.type != SOCK_STREAM)) { 17638c2ecf20Sopenharmony_ci log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); 17648c2ecf20Sopenharmony_ci return 1; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (args.prefix_len && !args.password) { 17688c2ecf20Sopenharmony_ci log_error("Prefix range for MD5 protection specified without a password\n"); 17698c2ecf20Sopenharmony_ci return 1; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if ((args.use_setsockopt || args.use_cmsg) && !args.ifindex) { 17738c2ecf20Sopenharmony_ci fprintf(stderr, "Device binding not specified\n"); 17748c2ecf20Sopenharmony_ci return 1; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci if (args.use_setsockopt || args.use_cmsg) 17778c2ecf20Sopenharmony_ci args.dev = NULL; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci if (iter == 0) { 17808c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid number of messages to send\n"); 17818c2ecf20Sopenharmony_ci return 1; 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (args.type == SOCK_STREAM && !args.protocol) 17858c2ecf20Sopenharmony_ci args.protocol = IPPROTO_TCP; 17868c2ecf20Sopenharmony_ci if (args.type == SOCK_DGRAM && !args.protocol) 17878c2ecf20Sopenharmony_ci args.protocol = IPPROTO_UDP; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if ((args.type == SOCK_STREAM || args.type == SOCK_DGRAM) && 17908c2ecf20Sopenharmony_ci args.port == 0) { 17918c2ecf20Sopenharmony_ci fprintf(stderr, "Invalid port number\n"); 17928c2ecf20Sopenharmony_ci return 1; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (!server_mode && !args.has_grp && 17968c2ecf20Sopenharmony_ci !args.has_remote_ip && !args.has_local_ip) { 17978c2ecf20Sopenharmony_ci fprintf(stderr, 17988c2ecf20Sopenharmony_ci "Local (server mode) or remote IP (client IP) required\n"); 17998c2ecf20Sopenharmony_ci return 1; 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (interactive) { 18038c2ecf20Sopenharmony_ci prog_timeout = 0; 18048c2ecf20Sopenharmony_ci msg = NULL; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci if (server_mode) { 18088c2ecf20Sopenharmony_ci do { 18098c2ecf20Sopenharmony_ci rc = do_server(&args); 18108c2ecf20Sopenharmony_ci } while (forever); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return rc; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci return do_client(&args); 18158c2ecf20Sopenharmony_ci} 1816