From ba062afbc2014dd38fa9461bc50e49d6911a70dc Mon Sep 17 00:00:00 2001
From: Joel Cunningham <joel.cunningham@me.com>
Date: Wed, 8 Apr 2015 11:07:40 -0500
Subject: [PATCH] Added sendmsg tests to socket examples

This commit adds a TCP and UDP test to exercise sendmsg correctness.
Use receiver and packet capture tool to verify results
---
 apps/socket_examples/socket_examples.c | 172 +++++++++++++++++++++++++++++++++
 1 file changed, 172 insertions(+)

diff --git a/apps/socket_examples/socket_examples.c b/apps/socket_examples/socket_examples.c
index a5f2c60..29cfe3a 100644
--- a/apps/socket_examples/socket_examples.c
+++ b/apps/socket_examples/socket_examples.c
@@ -5,6 +5,7 @@
 
 #if LWIP_SOCKET
 
+#include "lwip/mem.h"
 #include "lwip/sockets.h"
 #include "lwip/sys.h"
 
@@ -361,6 +362,177 @@ sockex_testrecv(void *arg)
   printf("sockex_testrecv finished successfully\n");
 }
 
+/** This is an example function that tests
+the sendmsg function for TCP sockets */
+static void
+sockex_testsendmsg_tcp(void *arg)
+{
+  int s;
+  int i;
+  int result;
+  int bytes_written;
+  u32_t opt;
+  struct sockaddr_in addr;
+  struct iovec iovs[4];
+  struct msghdr msg;
+  char * big_bytes;
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* set up address to send to */
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_len = sizeof(addr);
+  addr.sin_family = AF_INET;
+  addr.sin_port = PP_HTONS(SOCK_TARGET_PORT);
+  addr.sin_addr.s_addr = inet_addr(SOCK_TARGET_HOST);
+
+  /* for TCP, we will have a stream of 0xDEADBEEF repeated */
+  s = lwip_socket(AF_INET, SOCK_STREAM, 0);
+  LWIP_ASSERT("s >= 0", s >= 0);
+
+  /* connect, should succeed */
+  result = lwip_connect(s, (struct sockaddr*)&addr, sizeof(addr));
+  LWIP_ASSERT("result == 0", result == 0);
+
+  /* allocate a buffer for a stream of 0xDEADBEEF way bigger than the TCP's send buffer.
+  This will force execution of the partial IO vector send case when non-blocking */
+  big_bytes = mem_malloc(20000 * sizeof(*big_bytes));
+  LWIP_ASSERT("big_bytes != NULL", big_bytes != NULL);
+  for (i = 0; i < 20000; i += 4) {
+    big_bytes[i] = 0xDE;
+    big_bytes[i+1] = 0xAD;
+    big_bytes[i+2] = 0xBE;
+    big_bytes[i+3] = 0xEF;
+  }
+
+  /* send the big bytes buffer 4 times in one message */
+  for (i = 0; i < 4; i++) {
+    iovs[i].iov_base = big_bytes;
+    iovs[i].iov_len = 20000;
+  }
+
+  /* perform a blocking sendmsg, should return when all IO vectors have been accepted */
+  msg.msg_name = NULL; /* we are already connected */
+  msg.msg_namelen = 0;
+  msg.msg_iov = iovs;
+  msg.msg_iovlen = 4;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  result = lwip_sendmsg(s, &msg, 0);
+  LWIP_ASSERT("result == 80,000", result == 80000);
+  
+  /* repeat again, using non-blocking sockets.  This will hit fun cases where only part
+  of our IO vectors were accepted */
+  opt = lwip_fcntl(s, F_GETFL, 0);
+  LWIP_ASSERT("opt != -1", opt != -1);
+  opt |= O_NONBLOCK;
+  result = lwip_fcntl(s, F_SETFL, opt);
+  LWIP_ASSERT("result != -1", result != -1);
+
+  bytes_written = 0;
+  while (bytes_written < 80000) {
+    result = lwip_sendmsg(s, &msg, 0);
+    LWIP_ASSERT("sendmsg returned 0", (result != 0));
+    /* some data was sent */
+    if (result > 0) {
+      bytes_written += result;
+      if (bytes_written < 80000) {
+        /* process fully consumed vectors */
+        for (i = 0; i < msg.msg_iovlen; i++) {
+          if (msg.msg_iov[i].iov_len <= (u32_t)result) {
+            /* reduce result by amount of this vector */
+            result -= msg.msg_iov[i].iov_len;
+          } else /* iov not fully consumed */
+            break;
+        }
+
+        /* slide down over fully consumed vectors */
+        if (i != 0) {
+          msg.msg_iov = &msg.msg_iov[i];
+        }
+        msg.msg_iovlen -= i;
+
+        /* update new first vector */
+        msg.msg_iov[0].iov_base = ((u8_t *)msg.msg_iov[0].iov_base + result);
+        msg.msg_iov[0].iov_len -= result;
+      }
+    } else { /* error received on socket */
+      int optval;
+      socklen_t optlen = sizeof(optval);
+      fd_set write_fds;
+      
+      result = getsockopt(s, SOL_SOCKET, SO_ERROR, &optval, &optlen);
+      LWIP_ASSERT("getsockopt returned non-zero", (result == 0));
+      LWIP_ASSERT("SO_ERROR not EWOULDBLOCK", (optval == EWOULDBLOCK));
+
+      /* EWOULDBLOCK, use select to wait for send availability */
+      FD_ZERO(&write_fds);
+      FD_SET(s, &write_fds);
+      result = lwip_select(s+1, NULL, &write_fds, NULL, NULL);
+      LWIP_ASSERT("Error in select", (result > 0));
+      LWIP_ASSERT("Write FD not set", FD_ISSET(s, &write_fds));
+    }
+  }
+  /* verify 8,000 bytes received by peer, stream of 0xDEADBEEF */
+  close(s);
+}
+
+/** This is an example function that tests
+the sendmsg function for UDP sockets */
+static void
+sockex_testsendmsg_udp(void *arg)
+{
+  int s;
+  int i;
+  int result;
+  struct sockaddr_in addr;
+  struct iovec iovs[4];
+  struct msghdr msg;
+  char bytes[4];  
+
+  LWIP_UNUSED_ARG(arg);
+
+  /* each datagram should be 0xDEADBEEF */
+  bytes[0] = 0xDE;
+  bytes[1] = 0xAD;
+  bytes[2] = 0xBE;
+  bytes[3] = 0xEF;
+
+  /* initialize IO vectors with data */
+  for (i = 0; i < 4; i++) {
+    iovs[i].iov_base = &bytes[i];
+    iovs[i].iov_len = sizeof(char);
+  }
+
+  /* set up address to send to */
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_len = sizeof(addr);
+  addr.sin_family = AF_INET;
+  addr.sin_port = PP_HTONS(SOCK_TARGET_PORT);
+  addr.sin_addr.s_addr = inet_addr(SOCK_TARGET_HOST);
+
+  s = lwip_socket(AF_INET, SOCK_DGRAM, 0);
+  LWIP_ASSERT("s >= 0", s >= 0);
+
+  msg.msg_name = &addr;
+  msg.msg_namelen = sizeof(addr);
+  msg.msg_iov = iovs;
+  msg.msg_iovlen = 4;
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+
+  /* send our datagram of IO vectors 100 times */
+  for (i = 0; i < 100; i++) {
+    result = lwip_sendmsg(s, &msg, 0);
+    LWIP_ASSERT("result == 4", result == 4);
+    /* verify 0xDEADBEEF on receiver or network capture */
+  }
+  close(s);
+}
+
 /** helper struct for the 2 functions below (multithreaded: thread-argument) */
 struct sockex_select_helper {
   int socket;
-- 
1.9.5.msysgit.0

