Module: xenomai-3 Branch: next Commit: d190876cfb5feaafca477c797c324278e06cda93 URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=d190876cfb5feaafca477c797c324278e06cda93
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Sun Oct 11 16:05:18 2015 +0200 testsuite/smokey: add RTnet testsuite Starting with UDP and raw sockets. Measuring round trip time and checking for lost packets. --- configure.ac | 3 + testsuite/smokey/Makefile.am | 3 + testsuite/smokey/net_common/Makefile.am | 27 ++ testsuite/smokey/net_common/client.c | 286 ++++++++++++ testsuite/smokey/net_common/server.c | 178 ++++++++ testsuite/smokey/net_common/setup.c | 519 ++++++++++++++++++++++ testsuite/smokey/net_common/smokey_net.h | 51 +++ testsuite/smokey/net_common/smokey_net_server.c | 214 +++++++++ testsuite/smokey/net_common/smokey_net_server.h | 31 ++ testsuite/smokey/net_packet_dgram/Makefile.am | 10 + testsuite/smokey/net_packet_dgram/packet_dgram.c | 81 ++++ testsuite/smokey/net_udp/Makefile.am | 10 + testsuite/smokey/net_udp/udp.c | 75 ++++ 13 files changed, 1488 insertions(+) diff --git a/configure.ac b/configure.ac index c2fafb7..162e515 100644 --- a/configure.ac +++ b/configure.ac @@ -905,6 +905,9 @@ AC_CONFIG_FILES([ \ testsuite/smokey/tsc/Makefile \ testsuite/smokey/leaks/Makefile \ testsuite/smokey/fpu-stress/Makefile \ + testsuite/smokey/net_udp/Makefile \ + testsuite/smokey/net_packet_dgram/Makefile \ + testsuite/smokey/net_common/Makefile \ testsuite/clocktest/Makefile \ testsuite/xeno-test/Makefile \ utils/Makefile \ diff --git a/testsuite/smokey/Makefile.am b/testsuite/smokey/Makefile.am index 023cd47..0139349 100644 --- a/testsuite/smokey/Makefile.am +++ b/testsuite/smokey/Makefile.am @@ -11,6 +11,9 @@ COBALT_SUBDIRS = \ fpu-stress \ iddp \ leaks \ + net_packet_dgram\ + net_udp \ + net_common \ posix-clock \ posix-cond \ posix-fork \ diff --git a/testsuite/smokey/net_common/Makefile.am b/testsuite/smokey/net_common/Makefile.am new file mode 100644 index 0000000..a0c6522 --- /dev/null +++ b/testsuite/smokey/net_common/Makefile.am @@ -0,0 +1,27 @@ +bin_PROGRAMS = smokey_net_server + +noinst_LIBRARIES = libnet_common.a +noinst_HEADERS = \ + smokey_net.h \ + smokey_net_server.h + +AM_CPPFLAGS = \ + @XENO_USER_CFLAGS@ \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/kernel/drivers/net/stack/include + +libnet_common_a_SOURCES = \ + client.c \ + server.c \ + setup.c + +smokey_net_server_SOURCES = \ + smokey_net_server.c + +smokey_net_server_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ + +smokey_net_server_LDADD = \ + libnet_common.a \ + $(top_builddir)/lib/@XENO_CORE_LIB@ \ + @XENO_USER_LDADD@ \ + -lpthread -lrt diff --git a/testsuite/smokey/net_common/client.c b/testsuite/smokey/net_common/client.c new file mode 100644 index 0000000..0932825 --- /dev/null +++ b/testsuite/smokey/net_common/client.c @@ -0,0 +1,286 @@ +#include <stdio.h> +#include <stdbool.h> + +#include <unistd.h> +#include <pthread.h> +#include <sched.h> +#include <sys/socket.h> +#include <sys/select.h> + +#include "smokey_net.h" + +static int duration = 10; +static int rate = 1000; +static const char *driver = "rt_loopback"; +static const char *intf; +static pthread_t tid; +static unsigned long long glost, glate; + +static int rcv_packet(struct smokey_net_client *client, int sock, unsigned seq, + struct timespec *next_shot, bool last) +{ + static unsigned long long gmin = ~0ULL, gmax = 0, gsum = 0, gcount = 0; + static unsigned long long min = ~0ULL, max = 0, sum = 0, count = 0, + lost = 0, late = 0; + static struct timespec last_print; + struct smokey_net_payload payload; + struct timeval timeout; + struct timespec now; + char packet[256]; + long long diff; + fd_set set; + int err; + + FD_ZERO(&set); + FD_SET(sock, &set); + + err = smokey_check_errno( + __RT(clock_gettime(CLOCK_MONOTONIC, &now))); + if (err < 0) + return err; + + diff = next_shot->tv_sec * 1000000000ULL + next_shot->tv_nsec + - (now.tv_sec * 1000000000ULL + now.tv_nsec); + if (diff < 0) + diff = 0; + + timeout.tv_sec = diff / 1000000000; + timeout.tv_usec = (diff % 1000000000 + 500) / 1000; + + err = smokey_check_errno( + __RT(select(sock + 1, &set, NULL, NULL, &timeout))); + if (err < 0) + return err; + + if (err == 0) { + if (seq) + ++lost; + err = -ETIMEDOUT; + goto print_stats; + } + + err = smokey_check_errno( + __RT(recv(sock, packet, sizeof(packet), 0))); + if (err < 0) + return err; + + err = client->extract(client, &payload, packet, err); + if (err < 0) + return err; + + err = smokey_check_errno( + __RT(clock_gettime(CLOCK_MONOTONIC, &now))); + if (err < 0) + return err; + + diff = now.tv_sec * 1000000000ULL + now.tv_nsec + - (payload.ts.tv_sec * 1000000000ULL + + payload.ts.tv_nsec); + if (diff < min) + min = diff; + if (diff > max) + max = diff; + sum += diff; + ++count; + + err = 0; + if (payload.seq != seq) { + ++late; + err = -EAGAIN; + } + + print_stats: + if (seq == 1 && !last_print.tv_sec) { + last_print = now; + if (last_print.tv_nsec < 1000000000 / rate) { + last_print.tv_nsec += 1000000000; + last_print.tv_sec--; + } + last_print.tv_nsec -= 1000000000 / rate; + } + + diff = now.tv_sec * 1000000000ULL + now.tv_nsec + - (last_print.tv_sec * 1000000000ULL + + last_print.tv_nsec); + + if (diff < 1000000000LL && (!last || (!count && !lost))) + return err; + + if (min < gmin) + gmin = min; + if (max > gmax) + gmax = max; + gsum += sum; + gcount += count; + glost += lost - late; + glate += late; + + smokey_trace("%g pps\t%Lu\t%Lu\t%.03gus\t%.03gus\t%.03gus\t" + "| %Lu\t%Lu\t%.03gus\t%.03gus\t%.03gus", + count / (diff / 1000000000.0), + lost - late, + late, + count ? min / 1000.0 : 0, + count ? (sum / (double)count) / 1000 : 0, + count ? max / 1000.0 : 0, + glost, + glate, + gcount ? gmin / 1000.0 : 0, + gcount ? (gsum / (double)gcount) / 1000 : 0, + gcount ? gmax / 1000.0 : 0); + + min = ~0ULL; + max = 0; + sum = 0; + count = 0; + lost = 0; + late = 0; + last_print = now; + + return err; +} + +static int smokey_net_client_loop(struct smokey_net_client *client) +{ + struct smokey_net_payload payload; + struct timespec next_shot; + struct sched_param prio; + char packet[256]; + long long limit; + int sock, err; + + sock = client->create_socket(client); + if (sock < 0) + return sock; + + prio.sched_priority = 20; + err = smokey_check_status( + pthread_setschedparam(pthread_self(), SCHED_FIFO, &prio)); + if (err < 0) + return err; + + err = smokey_check_errno( + __RT(clock_gettime(CLOCK_MONOTONIC, &next_shot))); + if (err < 0) + goto err; + + limit = (long long)rate * duration; + for (payload.seq = 1; + limit <= 0 || payload.seq < limit + 1; payload.seq++) { + unsigned seq = payload.seq; + + next_shot.tv_nsec += 1000000000 / rate; + if (next_shot.tv_nsec > 1000000000) { + next_shot.tv_nsec -= 1000000000; + next_shot.tv_sec++; + } + + err = smokey_check_errno( + __RT(clock_gettime(CLOCK_MONOTONIC, &payload.ts))); + if (err < 0) + goto err; + + err = client->prepare(client, packet, sizeof(packet), &payload); + if (err < 0) + goto err; + + err = smokey_check_errno( + __RT(sendto(sock, packet, err, 0, + &client->peer, client->peer_len))); + if (err < 0) + goto err; + + do { + err = rcv_packet(client, sock, seq, &next_shot, + payload.seq == limit); + if (!err) + seq = 0; + } while (err != -ETIMEDOUT); + } + + if (glost || glate) + fprintf(stderr, "RTnet %s test failed", client->name); + if (glost) { + if (glost == limit) + fprintf(stderr, ", all packets lost" + " (is smokey_net_server running ?)"); + else + fprintf(stderr, ", %Lu packets lost (%g %%)", + glost, 100.0 * glost / limit); + } + if (glate) + fprintf(stderr, ", %Lu overruns", glate); + if (glost || glate) + fputc('\n', stderr); + err = glost || glate ? -EPROTO : 0; + + err: + sock = smokey_check_errno(__RT(close(sock))); + if (err == 0) + err = sock; + + return err; +} + +static void *trampoline(void *cookie) +{ + int err = smokey_net_client_loop(cookie); + pthread_exit((void *)(long)err); +} + +int smokey_net_client_run(struct smokey_test *t, + struct smokey_net_client *client, + int argc, char *const argv[]) +{ + int err, err_teardown; + void *status; + + smokey_parse_args(t, argc, argv); + + if (SMOKEY_ARG_ISSET(*t, rtnet_driver)) + driver = SMOKEY_ARG_STRING(*t, rtnet_driver); + + if (SMOKEY_ARG_ISSET(*t, rtnet_interface)) + intf = SMOKEY_ARG_STRING(*t, rtnet_interface); + + if (SMOKEY_ARG_ISSET(*t, rtnet_duration)) + duration = SMOKEY_ARG_INT(*t, rtnet_duration); + + if (SMOKEY_ARG_ISSET(*t, rtnet_rate)) { + rate = SMOKEY_ARG_INT(*t, rtnet_rate); + if (rate == 0) { + smokey_warning("rate can not be null"); + return -EINVAL; + } + } + + if (!intf) + intf = strcmp(driver, "rt_loopback") ? "rteth0" : "rtlo"; + + smokey_trace("Configuring interface %s (driver %s) for RTnet %s test", + intf, driver, client->name); + + err = smokey_net_setup(driver, intf, client->option, &client->peer); + if (err < 0) + return err; + + smokey_trace("Running RTnet %s test on interface %s", + client->name, intf); + + err = smokey_check_status( + __RT(pthread_create(&tid, NULL, trampoline, client))); + if (err < 0) + return err; + + err = smokey_check_status(pthread_join(tid, &status)); + if (err < 0) + return err; + + err = (int)(long)status; + + err_teardown = smokey_net_teardown(driver, intf, client->option); + if (err == 0) + err = err_teardown; + + return err; +} diff --git a/testsuite/smokey/net_common/server.c b/testsuite/smokey/net_common/server.c new file mode 100644 index 0000000..39d59c3 --- /dev/null +++ b/testsuite/smokey/net_common/server.c @@ -0,0 +1,178 @@ +/* + * RTnet test server loop + * + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <errno.h> +#include <string.h> + +#include <unistd.h> +#include <signal.h> +#include <pthread.h> +#include <sched.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> + +#include <sys/cobalt.h> +#include <smokey/smokey.h> +#include "smokey_net.h" +#include "smokey_net_server.h" + +struct smokey_server { + sem_t sync; + int fds[0]; +}; + +struct proto { + int config_flag; + int (*create_socket)(void); + void (*serve)(int fd); +}; + +static int udp_create_socket(void); +static void udp_serve(int fd); +static int packet_dgram_socket(void); +static void packet_dgram_serve(int fd); + +static const struct proto protos[] = { + { + .config_flag = _CC_COBALT_NET_UDP, + .create_socket = &udp_create_socket, + .serve = &udp_serve, + }, + { + .config_flag = _CC_COBALT_NET_AF_PACKET, + .create_socket = &packet_dgram_socket, + .serve = &packet_dgram_serve, + }, +}; + +static int udp_create_socket(void) +{ + struct sockaddr_in name; + int fd; + + fd = check_unix(__RT(socket(PF_INET, SOCK_DGRAM, 0))); + + name.sin_family = AF_INET; + name.sin_port = htons(7); /* UDP echo service */ + name.sin_addr.s_addr = htonl(INADDR_ANY); + + check_unix(__RT(bind(fd, (struct sockaddr *)&name, sizeof(name)))); + + return fd; +} + +static void udp_serve(int fd) +{ + struct smokey_net_payload pkt; + struct sockaddr_in peer; + socklen_t peer_len; + int err; + + peer_len = sizeof(peer); + err = check_unix( + __RT(recvfrom(fd, &pkt, sizeof(pkt), 0, + (struct sockaddr *)&peer, &peer_len))); + + check_unix( + __RT(sendto(fd, &pkt, err, 0, + (struct sockaddr *)&peer, peer_len))); +} + +static int packet_dgram_socket(void) +{ + return check_unix( + __RT(socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_802_EX1)))); +} + +static void packet_dgram_serve(int fd) +{ + struct smokey_net_payload pkt; + struct sockaddr_ll peer; + socklen_t peer_len; + int err; + + peer_len = sizeof(peer); + err = check_unix( + __RT(recvfrom(fd, &pkt, sizeof(pkt), 0, + (struct sockaddr *)&peer, &peer_len))); + + peer.sll_protocol = htons(ETH_P_802_EX1 + 1); + check_unix( + __RT(sendto(fd, &pkt, err, 0, + (struct sockaddr *)&peer, peer_len))); +} + +static void server_loop_cleanup(void *cookie) +{ + int *fds = cookie; + int i; + + for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) + __RT(close(fds[i])); + free(fds); +} + +void smokey_net_server_loop(int net_config) +{ + struct sched_param prio; + const struct proto *p; + int i, maxfd, *fds; + fd_set rfds; + + fds = malloc(sizeof(*fds) * sizeof(protos)/sizeof(protos[0])); + if (fds == NULL) + pthread_exit((void *)(long)-ENOMEM); + + pthread_cleanup_push(server_loop_cleanup, fds); + + FD_ZERO(&rfds); + maxfd = 0; + for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) { + p = &protos[i]; + + if ((net_config & p->config_flag) == 0) { + fds[i] = -1; + continue; + } + + fds[i] = p->create_socket(); + FD_SET(fds[i], &rfds); + if (fds[i] > maxfd) + maxfd = fds[i]; + } + + prio.sched_priority = 20; + check_pthread( + __RT(pthread_setschedparam(pthread_self(), SCHED_FIFO, &prio))); + + for (;;) { + fd_set tfds; + + tfds = rfds; + + check_unix(__RT(select(maxfd + 1, &tfds, NULL, NULL, NULL))); + + for (i = 0; i < sizeof(protos)/sizeof(protos[0]); i++) { + p = &protos[i]; + + if (fds[i] < 0 || !FD_ISSET(fds[i], &tfds)) + continue; + + p->serve(fds[i]); + } + } + + pthread_cleanup_pop(1); +} diff --git a/testsuite/smokey/net_common/setup.c b/testsuite/smokey/net_common/setup.c new file mode 100644 index 0000000..5e83621 --- /dev/null +++ b/testsuite/smokey/net_common/setup.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/ether.h> +#include <netpacket/packet.h> + +#include <sys/cobalt.h> +#include <smokey/smokey.h> + +#include <rtnet_chrdev.h> +#include <rtcfg_chrdev.h> +#include "smokey_net.h" +#include "smokey_net_server.h" + + +struct module { + int option; + const char *name; +}; + +#define TIMEOUT 10 + +static struct rtnet_core_cmd cmd; +static int fd; +static pthread_t loopback_server_tid; +static bool loopback_thread_created; +static struct module modules[] = { + { + .option = _CC_COBALT_NET_UDP, + .name = "rtudp", + }, + { + .option = _CC_COBALT_NET_AF_PACKET, + .name = "rtpacket", + }, +}; + +static const char *option_to_module(int option) +{ + unsigned i; + + for (i = 0; i < sizeof(modules)/sizeof(modules[0]); i++) { + if (modules[i].option != option) + continue; + + return modules[i].name; + } + + return NULL; +} + +static int get_info(const char *intf) +{ + int err; + + err = smokey_check_errno( + snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + "%s", intf)); + if (err < 0) + return err; + + cmd.args.info.ifindex = 0; + + err = smokey_check_errno(ioctl(fd, IOC_RT_IFINFO, &cmd)); + if (err < 0) + return err; + + return 0; +} + +static int do_up(const char *intf) +{ + int err; + + snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), "%s", intf); + cmd.args.info.ifindex = 0; + if (strcmp(intf, "rtlo")) { + cmd.args.up.ip_addr = 0xffffffff; + cmd.args.up.broadcast_ip = cmd.args.up.ip_addr; + } else { + cmd.args.up.ip_addr = htonl(0x7f000001); /* 127.0.0.1 */ + cmd.args.up.broadcast_ip = cmd.args.up.ip_addr | ~0x000000ff; + } + cmd.args.up.set_dev_flags = 0; + cmd.args.up.clear_dev_flags = 0; + cmd.args.up.dev_addr_type = 0xffff; + + err = smokey_check_errno(ioctl(fd, IOC_RT_IFUP, &cmd)); + if (err < 0) + return err; + + return 0; +} + +static int do_down(const char *intf) +{ + int err; + + snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), "%s", intf); + cmd.args.info.ifindex = 0; + + err = smokey_check_errno(ioctl(fd, IOC_RT_IFDOWN, &cmd)); + if (err < 0) + return err; + + return 0; +} + +static int smokey_net_modprobe(const char *mod) +{ + char buffer[128]; + int err; + + err = smokey_check_errno( + snprintf(buffer, sizeof(buffer), "modprobe %s", mod)); + if (err < 0) + return err; + + err = smokey_check_errno(system(buffer)); + if (err < 0) + return err; + + if (!WIFEXITED(err) || WEXITSTATUS(err) != 0) { + smokey_warning("%s: abnormal exit", buffer); + return -EINVAL; + } + + return err; +} + +static int smokey_net_rmmod(const char *mod) +{ + char buffer[128]; + int err; + + err = smokey_check_errno( + snprintf(buffer, sizeof(buffer), "rmmod %s", mod)); + if (err < 0) + return err; + + err = smokey_check_errno(system(buffer)); + if (err < 0) + return err; + + if (!WIFEXITED(err) || WEXITSTATUS(err) != 0) { + smokey_warning("%s: abnormal exit", buffer); + return -EINVAL; + } + + return err; +} + +static int smokey_net_setup_rtcfg_client(const char *intf, int net_config) +{ + struct rtcfg_cmd cmd; + int err; + + if ((net_config & _CC_COBALT_NET_CFG) == 0) + return -ENOSYS; + + err = smokey_net_modprobe("rtcfg"); + if (err < 0) + return err; + + memset(&cmd, 0, sizeof(cmd)); + err = smokey_check_errno( + snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + intf, IFNAMSIZ)); + if (err < 0) + return err; + + cmd.args.client.timeout = 10000; + cmd.args.client.max_stations = 32; + cmd.args.client.buffer_size = 0; + + err = smokey_check_errno(ioctl(fd, RTCFG_IOC_CLIENT, &cmd)); + if (err < 0) + return err; + + cmd.args.announce.timeout = 5000; + cmd.args.announce.buffer_size = 0; + cmd.args.announce.flags = 0; + cmd.args.announce.burstrate = 4; + + err = smokey_check_errno(ioctl(fd, RTCFG_IOC_ANNOUNCE, &cmd)); + if (err < 0) + return err; + + return 0; +} + +static int smokey_net_teardown_rtcfg(const char *intf) +{ + struct rtcfg_cmd cmd; + int err; + + memset(&cmd, 0, sizeof(cmd)); + err = smokey_check_errno( + snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + intf, IFNAMSIZ)); + if (err < 0) + return err; + + err = smokey_check_errno(ioctl(fd, RTCFG_IOC_DETACH, &cmd)); + if (err < 0) + return err; + + return smokey_net_rmmod("rtcfg"); +} + +static int find_peer(const char *intf, void *vpeer) +{ + struct sockaddr_in *in_peer = vpeer; + struct sockaddr_ll *ll_peer = vpeer; + struct sockaddr *peer = vpeer; + char buf[4096]; + char hash[3]; + char dest[16]; + char mac[18]; + char dev[16]; + FILE *f; + int err; + + f = fopen("/proc/rtnet/ipv4/host_route", "r"); + if (!f) { + err = -errno; + smokey_warning("open(/proc/rtnet/ipv4/host_route): %s", + strerror(-err)); + return err; + } + + /* Skip first line */ + if (!fgets(buf, sizeof(buf), f)) { + err = -errno; + smokey_warning("fgets(/proc/rtnet/ipv4/host_route): %s", + strerror(-err)); + goto err; + } + + for(;;) { + err = fscanf(f, "%s\t%s\t%s\t%s\n", hash, dest, mac, dev); + if (err == 0) { + smokey_warning("No peer found\n"); + err = -ENOENT; + goto err; + } + if (err < 0) { + err = -errno; + smokey_warning("fscanf: %s", strerror(-err)); + goto err; + } + if (err < 4) { + smokey_warning("Error parsing" + " /proc/rtnet/ipv4/host_route\n"); + err = -EINVAL; + goto err; + } + + if (strcmp(dev, intf)) + continue; + + if (strcmp(mac, "FF:FF:FF:FF:FF:FF") == 0) + continue; + + if (strcmp(dest, "255.255.255.255") == 0) + continue; + + if (strcmp(dest, "0.0.0.0") == 0) + continue; + + break; + } + + switch(peer->sa_family) { + case AF_INET: + err = smokey_check_errno( + inet_pton(AF_INET, dest, &in_peer->sin_addr)); + if (err < 0) + goto err; + break; + + case AF_PACKET: { + const unsigned eth_alen = 6; + struct ether_addr eth; + struct ifreq ifr; + int sock; + + ll_peer->sll_halen = eth_alen; + if (ether_aton_r(mac, ð) == 0) { + err = -errno; + smokey_warning("ether_aton_r(%s): %m", mac); + goto err; + } + + memcpy(&ll_peer->sll_addr[0], eth.ether_addr_octet, eth_alen); + + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", dev); + + err = smokey_check_errno( + __RT(socket(PF_PACKET, SOCK_DGRAM, 0))); + if (err < 0) + goto err; + sock = err; + + err = smokey_check_errno(__RT(ioctl(sock, SIOCGIFINDEX, &ifr))); + sock = smokey_check_errno(__RT(close(sock))); + if (err < 0) + goto err; + if (sock < 0) { + err = sock; + goto err; + } + + ll_peer->sll_ifindex = ifr.ifr_ifindex; + } + } + + err = 0; + err: + fclose(f); + return err; +} + +int smokey_net_server_check_inner(const char *file, int line, + const char *msg, int status) +{ + if (status >= 0) + return status; + + __smokey_warning(file, line, "%s: %s", msg, strerror(-status)); + pthread_exit((void *)(long)status); +} + +static void *loopback_server(void *cookie) +{ + int net_config = (long)cookie; + smokey_net_server_loop(net_config); + return NULL; +} + +int smokey_net_setup(const char *driver, const char *intf, int tested_config, + void *vpeer) +{ + int net_config, err, i, err_teardown; + struct sockaddr_in *in_peer = vpeer; + struct sockaddr *peer = vpeer; + + err = cobalt_corectl(_CC_COBALT_GET_NET_CONFIG, + &net_config, sizeof(net_config)); + if (err == -EINVAL) + return -ENOSYS; + if (err < 0) + return err; + + if ((net_config & (_CC_COBALT_NET | _CC_COBALT_NET_IPV4)) + != (_CC_COBALT_NET | _CC_COBALT_NET_IPV4)) + return -ENOSYS; + + if ((net_config & tested_config) == 0) + return -ENOSYS; + + err = smokey_net_modprobe(driver); + if (err < 0) + return err; + + err = smokey_net_modprobe("rtipv4"); + if (err < 0) + return err; + + err = smokey_net_modprobe(option_to_module(tested_config)); + if (err < 0) + return err; + + fd = smokey_check_errno(open("/dev/rtnet", O_RDWR)); + if (fd < 0) + return fd; + + err = get_info(intf); + if (err < 0) + goto err; + + if ((cmd.args.info.flags & IFF_UP) == 0) { + err = do_up(intf); + if (err < 0) + goto err; + } + + smokey_trace("Waiting for interface %s to be running", intf); + + for (i = 0; i < 30; i++) { + err = get_info(intf); + if (err < 0) + goto err; + + if ((cmd.args.info.flags & (IFF_UP | IFF_RUNNING)) + == (IFF_UP | IFF_RUNNING)) + goto running; + + sleep(1); + } + + smokey_warning("Interface is not running," + " giving up (cable unplugged?)"); + err = -ETIMEDOUT; + goto err; + +running: + err = get_info(intf); + if (err < 0) + goto err; + + if (cmd.args.info.ip_addr == 0) { + err = smokey_net_setup_rtcfg_client(intf, net_config); + if (err < 0) + goto err; + } + + if (strcmp(driver, "rt_loopback") == 0) { + err = smokey_check_status( + __RT(pthread_create(&loopback_server_tid, NULL, + loopback_server, + (void *)(long)tested_config))); + if (err < 0) + goto err; + loopback_thread_created = true; + } + + switch (peer->sa_family) { + case AF_INET: + if (in_peer->sin_addr.s_addr == htonl(INADDR_ANY) && + strcmp(driver, "rt_loopback") == 0) { + in_peer->sin_addr.s_addr = cmd.args.info.ip_addr; + break; + } + + /* Fallthrough wanted */ + case AF_PACKET: + err = find_peer(intf, vpeer); + if (err < 0) + goto err; + } + + close(fd); + return 0; + + err: + close(fd); + + err_teardown = smokey_net_teardown(driver, intf, tested_config); + if (err == 0) + err = err_teardown; + + return err; +} + +int smokey_net_teardown(const char *driver, const char *intf, int tested_config) +{ + int err = 0, tmp; + + if (loopback_thread_created) { + void *status; + + pthread_cancel(loopback_server_tid); /* May fail */ + tmp = smokey_check_errno( + pthread_join(loopback_server_tid, &status)); + if (err == 0) + err = tmp; + if (err == 0 && status != PTHREAD_CANCELED) + err = (long)status; + } + + tmp = smokey_check_errno(open("/dev/rtnet", O_RDWR)); + if (tmp >= 0) { + fd = tmp; + + if (strcmp(driver, "rt_loopback")) { + tmp = smokey_net_teardown_rtcfg(intf); + if (err == 0) + err = tmp; + } + + tmp = do_down(intf); + if (err == 0) + err = tmp; + + close(fd); + } else + err = tmp; + + tmp = smokey_net_rmmod(option_to_module(tested_config)); + if (err == 0) + err = tmp; + + tmp = smokey_net_rmmod(driver); + if (err == 0) + err = tmp; + + tmp = smokey_net_rmmod("rtipv4"); + if (err == 0) + err = tmp; + + tmp = smokey_net_rmmod("rtnet"); + if (err == 0) + err = tmp; + + return err; +} diff --git a/testsuite/smokey/net_common/smokey_net.h b/testsuite/smokey/net_common/smokey_net.h new file mode 100644 index 0000000..4b8dddd --- /dev/null +++ b/testsuite/smokey/net_common/smokey_net.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#ifndef SMOKEY_NET_H +#define SMOKEY_NET_H + +#include <time.h> +#include <sys/socket.h> +#include <netpacket/packet.h> +#include <netinet/in.h> + +#include <smokey/smokey.h> + +struct smokey_net_payload { + struct timespec ts; + unsigned seq; +}; + +struct smokey_net_client { + const char *name; + int option; + union { + struct sockaddr peer; + struct sockaddr_ll ll_peer; + struct sockaddr_in in_peer; + }; + socklen_t peer_len; + + int (*create_socket)(struct smokey_net_client *client); + int (*prepare)(struct smokey_net_client *client, + void *buf, size_t len, + const struct smokey_net_payload *payload); + int (*extract)(struct smokey_net_client *client, + struct smokey_net_payload *payload, + const void *buf, size_t len); +}; + +int smokey_net_setup(const char *driver, const char *intf, int tested_config, + void *vpeer); + +int smokey_net_teardown(const char *driver, + const char *intf, int tested_config); + +int smokey_net_client_run(struct smokey_test *t, + struct smokey_net_client *client, + int argc, char *const argv[]); + +#endif /* SMOKEY_NET_H */ diff --git a/testsuite/smokey/net_common/smokey_net_server.c b/testsuite/smokey/net_common/smokey_net_server.c new file mode 100644 index 0000000..a0ed52f --- /dev/null +++ b/testsuite/smokey/net_common/smokey_net_server.c @@ -0,0 +1,214 @@ +/* + * RTnet test server + * + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <unistd.h> +#include <getopt.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/ether.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <rtcfg_chrdev.h> +#include <sys/cobalt.h> +#include <smokey/smokey.h> +#include <xenomai/init.h> +#include "smokey_net.h" +#include "smokey_net_server.h" + +static const char *intf = "rteth0"; + +int smokey_net_server_check_inner(const char *file, int line, + const char *msg, int status) +{ + if (status >= 0) + return status; + + fprintf(stderr, "FAILED %s: returned error %d - %s\n", + msg, -status, strerror(-status)); + exit(EXIT_FAILURE); +} + +static int rtnet_rtcfg_setup_server(void) +{ + struct rtcfg_cmd cmd; + int fd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.args.server.period = 1000; + cmd.args.server.burstrate = 4; + cmd.args.server.heartbeat = 1000; + cmd.args.server.threshold = 2; + cmd.args.server.flags = 0; + + check_unix(snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + intf, sizeof(cmd.head.if_name))); + + fd = check_unix(open("/dev/rtnet", O_RDWR)); + + check_unix(ioctl(fd, RTCFG_IOC_SERVER, &cmd)); + + return fd; +} + +static void +rtnet_rtcfg_add_client(int fd, const char *hwaddr, const char *ipaddr) +{ + struct rtcfg_cmd cmd; + struct ether_addr mac; + struct in_addr ip; + + fprintf(stderr, "add client %s, mac %s\n", ipaddr, hwaddr); + + memset(&cmd, 0, sizeof(cmd)); + + check_unix(snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + intf, sizeof(cmd.head.if_name))); + + if (ether_aton_r(hwaddr, &mac) == 0) { + fprintf(stderr, "%s is an invalid mac address\n", hwaddr); + exit(EXIT_FAILURE); + } + + if (check_unix(inet_aton(ipaddr, &ip)) == 0) { + fprintf(stderr, "%s is an invalid ip address\n", ipaddr); + exit(EXIT_FAILURE); + } + + cmd.args.add.addr_type = RTCFG_ADDR_IP | FLAG_ASSIGN_ADDR_BY_MAC; + cmd.args.add.ip_addr = ip.s_addr; + cmd.args.add.timeout = 3000; + memcpy(cmd.args.add.mac_addr, mac.ether_addr_octet, + sizeof(cmd.args.add.mac_addr)); + + check_unix(ioctl(fd, RTCFG_IOC_ADD, &cmd)); +} + +static void cleanup(int sig) +{ + struct rtcfg_cmd cmd; + int fd; + + memset(&cmd, 0, sizeof(cmd)); + + check_unix(snprintf(cmd.head.if_name, sizeof(cmd.head.if_name), + intf, sizeof(cmd.head.if_name))); + + fd = check_unix(open("/dev/rtnet", O_RDWR)); + + check_unix(ioctl(fd, RTCFG_IOC_DETACH, &cmd)); + + close(fd); + + signal(sig, SIG_DFL); + raise(sig); +} + +void application_usage(void) +{ + fprintf(stderr, "%s options [ <interface> ]:\n\n" + "Runs server for smokey network tests, on interface named " + "<interface>\n" + "(rtlo if unspecified)\n\n" + "Available options:\n" + "-f | --file <file>\t\tAnswers clients from file named <file>" + "\n\t(uses standard input if unspecified)\n" + "\tWhere every line contains a mac address and an IP address\n", + get_program_name()); +} + +int main(int argc, char *argv[]) +{ + int net_config, c, fd, err; + FILE *input = stdin; + + check_native(cobalt_corectl(_CC_COBALT_GET_NET_CONFIG, + &net_config, sizeof(net_config))); + + for (;;) { + int option_index = 0; + + static struct option long_options[] = { + { "help", no_argument, 0, 'h', }, + { "file", required_argument, 0, 'f', }, + { 0, 0, 0, 0, }, + }; + + c = getopt_long(argc, argv, "hf:", long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 'h': + application_usage(); + exit(EXIT_SUCCESS); + + case 'f': + input = fopen(optarg, "r"); + if (input == NULL) { + fprintf(stderr, "fopen(%s): %m\n", optarg); + exit(EXIT_FAILURE); + } + break; + + case '?': + application_usage(); + exit(EXIT_FAILURE); + } + } + + if (optind < argc) { + if (argc - optind > 1) { + application_usage(); + printf("\nOnly one interface argument expected\n"); + exit(EXIT_FAILURE); + } + + intf = argv[optind]; + if (strcmp(intf, "rtlo") == 0) { + application_usage(); + printf("\nRunning smokey_net_server on rtlo makes no sense\n"); + exit(EXIT_FAILURE); + } + } + + if ((net_config & _CC_COBALT_NET_CFG) == 0) { + fprintf(stderr, "RTcfg not enabled, aborting\n"); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "Smokey network tests server, using interface %s\n", + intf); + + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + signal(SIGHUP, cleanup); + + fd = rtnet_rtcfg_setup_server(); + + do { + char *mac, *ip; + + err = fscanf(input, "%ms %ms\n", &mac, &ip); + if (err == 2) { + rtnet_rtcfg_add_client(fd, mac, ip); + free(mac); + free(ip); + } + } while (err != EOF); + + close(fd); + + smokey_net_server_loop(net_config); + exit(EXIT_SUCCESS); +} diff --git a/testsuite/smokey/net_common/smokey_net_server.h b/testsuite/smokey/net_common/smokey_net_server.h new file mode 100644 index 0000000..34d5692 --- /dev/null +++ b/testsuite/smokey/net_common/smokey_net_server.h @@ -0,0 +1,31 @@ +/* + * RTnet test server + * + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#ifndef SMOKEY_NET_CHECK_H +#define SMOKEY_NET_CHECK_H + +#define check_native(expr) \ + smokey_net_server_check_inner(__FILE__, __LINE__, #expr, (expr)) + +#define check_pthread(expr) \ + smokey_net_server_check_inner(__FILE__, __LINE__, #expr, -(expr)) + +#define check_unix(expr) \ + ({ \ + int s = (expr); \ + smokey_net_server_check_inner(__FILE__, __LINE__, #expr, s < 0 ? -errno : s); \ + }) + +struct smokey_server; + +int smokey_net_server_check_inner(const char *file, int line, + const char *msg, int status); + +void smokey_net_server_loop(int net_config); + +#endif /* SMOKEY_NET_CHECK_H */ diff --git a/testsuite/smokey/net_packet_dgram/Makefile.am b/testsuite/smokey/net_packet_dgram/Makefile.am new file mode 100644 index 0000000..257cbbe --- /dev/null +++ b/testsuite/smokey/net_packet_dgram/Makefile.am @@ -0,0 +1,10 @@ +noinst_LIBRARIES = libnet_packet_dgram.a + +libnet_packet_dgram_a_SOURCES = \ + packet_dgram.c + +libnet_packet_dgram_a_CPPFLAGS = \ + @XENO_USER_CFLAGS@ \ + -I$(srcdir)/../net_common \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/kernel/drivers/net/stack/include diff --git a/testsuite/smokey/net_packet_dgram/packet_dgram.c b/testsuite/smokey/net_packet_dgram/packet_dgram.c new file mode 100644 index 0000000..dc716a4 --- /dev/null +++ b/testsuite/smokey/net_packet_dgram/packet_dgram.c @@ -0,0 +1,81 @@ +/* + * RTnet AF_PACKET test + * + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#include <unistd.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <net/ethernet.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netpacket/packet.h> + +#include <sys/cobalt.h> +#include <smokey/smokey.h> +#include "smokey_net.h" + +smokey_test_plugin(net_packet_dgram, + SMOKEY_ARGLIST( + SMOKEY_STRING(rtnet_driver), + SMOKEY_STRING(rtnet_interface), + SMOKEY_INT(rtnet_rate), + SMOKEY_INT(rtnet_duration), + ), + "Check RTnet driver, using cooked packets, measuring round trip time\n" + "\tand packet losses,\n" + "\tthe rtnet_driver parameter allows choosing the network driver\n" + "\tthe rtnet_interface parameter allows choosing the network interface\n" + "\tthe rtnet_rate parameter allows choosing the packet rate\n" + "\tthe rtnet_duration parameter allows choosing the test duration\n" + "\tA server on the network must run the smokey_rtnet_server program." +); + +static int +packet_dgram_create_socket(struct smokey_net_client *client) +{ + return smokey_check_errno( + __RT(socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_802_EX1 + 1)))); +} + +static int +packet_dgram_prepare(struct smokey_net_client *client, + void *buf, size_t len, const struct smokey_net_payload *payload) +{ + if (sizeof(*payload) < len) + len = sizeof(*payload); + memcpy(buf, payload, len); + return len; +} + +static int +packet_dgram_extract(struct smokey_net_client *client, + struct smokey_net_payload *payload, const void *buf, size_t len) +{ + if (sizeof(*payload) < len) + len = sizeof(*payload); + memcpy(payload, buf, len); + return len; +} + +static int +run_net_packet_dgram(struct smokey_test *t, int argc, char *const argv[]) +{ + struct smokey_net_client client = { + .name = "cooked packets", + .option = _CC_COBALT_NET_AF_PACKET, + .create_socket = &packet_dgram_create_socket, + .prepare = &packet_dgram_prepare, + .extract = &packet_dgram_extract, + }; + + memset(&client.ll_peer, '\0', sizeof(client.ll_peer)); + client.ll_peer.sll_family = AF_PACKET; + client.ll_peer.sll_protocol = htons(ETH_P_802_EX1); + client.peer_len = sizeof(client.ll_peer); + + return smokey_net_client_run(t, &client, argc, argv); +} diff --git a/testsuite/smokey/net_udp/Makefile.am b/testsuite/smokey/net_udp/Makefile.am new file mode 100644 index 0000000..576a00c --- /dev/null +++ b/testsuite/smokey/net_udp/Makefile.am @@ -0,0 +1,10 @@ +noinst_LIBRARIES = libnet_udp.a + +libnet_udp_a_SOURCES = \ + udp.c + +libnet_udp_a_CPPFLAGS = \ + @XENO_USER_CFLAGS@ \ + -I$(srcdir)/../net_common \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/kernel/drivers/net/stack/include diff --git a/testsuite/smokey/net_udp/udp.c b/testsuite/smokey/net_udp/udp.c new file mode 100644 index 0000000..8ba89c4 --- /dev/null +++ b/testsuite/smokey/net_udp/udp.c @@ -0,0 +1,75 @@ +/* + * RTnet UDP test + * + * Copyright (C) 2015 Gilles Chanteperdrix <g...@xenomai.org> + * + * SPDX-License-Identifier: MIT + */ + +#include <netinet/in.h> + +#include <sys/cobalt.h> +#include <smokey/smokey.h> +#include "smokey_net.h" + +smokey_test_plugin(net_udp, + SMOKEY_ARGLIST( + SMOKEY_STRING(rtnet_driver), + SMOKEY_STRING(rtnet_interface), + SMOKEY_INT(rtnet_rate), + SMOKEY_INT(rtnet_duration), + ), + "Check RTnet driver, using UDP packets, measuring round trip time\n" + "\tand packet losses,\n" + "\tthe rtnet_driver parameter allows choosing the network driver\n" + "\tthe rtnet_interface parameter allows choosing the network interface\n" + "\tthe rtnet_rate parameter allows choosing the packet rate\n" + "\tthe rtnet_duration parameter allows choosing the test duration\n" + "\tA server on the network must run the smokey_rtnet_server program." +); + +static int +udp_create_socket(struct smokey_net_client *client) +{ + return smokey_check_errno(__RT(socket(PF_INET, SOCK_DGRAM, 0))); +} + +static int +udp_prepare(struct smokey_net_client *client, + void *buf, size_t len, const struct smokey_net_payload *payload) +{ + if (sizeof(*payload) < len) + len = sizeof(*payload); + memcpy(buf, payload, len); + return len; +} + +static int +udp_extract(struct smokey_net_client *client, + struct smokey_net_payload *payload, const void *buf, size_t len) +{ + if (sizeof(*payload) < len) + len = sizeof(*payload); + memcpy(payload, buf, len); + return len; +} + +static int +run_net_udp(struct smokey_test *t, int argc, char *const argv[]) +{ + struct smokey_net_client client = { + .name = "UDP", + .option = _CC_COBALT_NET_UDP, + .create_socket = &udp_create_socket, + .prepare = &udp_prepare, + .extract = &udp_extract, + }; + + memset(&client.in_peer, '\0', sizeof(client.in_peer)); + client.in_peer.sin_family = AF_INET; + client.in_peer.sin_port = htons(7); /* UDP echo port */ + client.in_peer.sin_addr.s_addr = htonl(INADDR_ANY); + client.peer_len = sizeof(client.in_peer); + + return smokey_net_client_run(t, &client, argc, argv); +} _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://xenomai.org/mailman/listinfo/xenomai-git