Module: xenomai-3
Branch: next
Commit: 5c8129cd03a55a78bac1a7a9d0536c9c462440bd
URL:    
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=5c8129cd03a55a78bac1a7a9d0536c9c462440bd

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          |   25 ++
 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, 1486 insertions(+)

diff --git a/configure.ac b/configure.ac
index 89c2911..5364b4f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -900,6 +900,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..614c709
--- /dev/null
+++ b/testsuite/smokey/net_common/Makefile.am
@@ -0,0 +1,25 @@
+bin_PROGRAMS = smokey_net_server
+
+noinst_LIBRARIES = libnet_common.a
+noinst_HEADERS = smokey_net.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, &eth) == 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

Reply via email to