LDPRELOAD: Add ioctl, fcntl, and setsockopt support

Add support for the following system calls:

ioctl (FIONREAD)
fcntl (F_GETFL)
fcntl (F_SETFL)
setsockopt (SOL_IPV6, IPV6_V6ONLY)
setsockopt (SOL_TCP, TCP_NODELAY)
setsockopt (SOL_SOCKET, SO_REUSEADDR)
setsockopt (SOL_SOCKET, SO_BROADCAST)

This patch supersedes https://gerrit.fd.io/r/#/c/8765/

Change-Id: I5d5309d9f43d93a990b389d8cb667631de1903fe
Signed-off-by: Steven <sluong@cisco.com>
diff --git a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.c b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.c
index 86b923c..5378548 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.c
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.c
@@ -18,6 +18,7 @@
 #include <limits.h>
 #define __need_IOV_MAX
 #include <bits/stdio_lim.h>
+#include <netinet/tcp.h>
 
 #include <vppinfra/types.h>
 #include <vppinfra/hash.h>
@@ -653,8 +654,6 @@
       /* Fallthrough */
     case F_GETFD:
     case F_SETFD:
-    case F_GETFL:
-    case F_SETFL:
     case F_GETLK:
     case F_SETLK:
     case F_SETLKW:
@@ -662,25 +661,36 @@
     case F_SETOWN:
       return 2;
 
-#if 0
-      /* cmd handled by vppcom */
-    case F_XXXXX:
+      /* cmd handled by vcom and vppcom */
+    case F_SETFL:
+    case F_GETFL:
       return 3;
-#endif
-      /* invalid cmd */
+
+      /* cmd not handled by vcom and vppcom */
     default:
-      return 0;
+      return 1;
     }
   return 0;
 }
 
-/* TBD: move it to vppcom */
 static int
-vppcom_session_fcntl_va (int __fd, int __cmd, va_list __ap)
+vppcom_session_fcntl_va (int __sid, int __cmd, va_list __ap)
 {
-  int rv;
+  int flags = va_arg (__ap, int);
+  int rv = -EOPNOTSUPP;
+  uint32_t size;
 
-  rv = -EINVAL;
+  size = sizeof (flags);
+  if (__cmd == F_SETFL)
+    {
+      rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_FLAGS, &flags, &size);
+    }
+  else if (__cmd == F_GETFL)
+    {
+      rv = vppcom_session_attr (__sid, VPPCOM_ATTR_GET_FLAGS, &flags, &size);
+      if (rv == VPPCOM_OK)
+	rv = flags;
+    }
 
   return rv;
 }
@@ -712,7 +722,7 @@
       break;
       /*cmd not handled by vcom and vppcom */
     case 1:
-      rv = -EBADF;
+      rv = libc_vfcntl (vsock->fd, __cmd, __ap);
       break;
       /* cmd handled by vcom socket resource */
     case 2:
@@ -731,6 +741,93 @@
   return rv;
 }
 
+/*
+ * RETURN:  0 - invalid cmd
+ *          1 - cmd not handled by vcom and vppcom
+ *          2 - cmd handled by vcom socket resource
+ *          3 - cmd handled by vppcom
+ */
+static int
+vcom_socket_check_ioctl_cmd (unsigned long int __cmd)
+{
+  int rc;
+
+  switch (__cmd)
+    {
+      /* cmd handled by vppcom */
+    case FIONREAD:
+      rc = 3;
+      break;
+
+      /* cmd not handled by vcom and vppcom */
+    default:
+      rc = 1;
+      break;
+    }
+  return rc;
+}
+
+static int
+vppcom_session_ioctl_va (int __sid, int __cmd, va_list __ap)
+{
+  int rv;
+
+  if (__cmd == FIONREAD)
+    rv = vppcom_session_attr (__sid, VPPCOM_ATTR_GET_NREAD, 0, 0);
+  else
+    rv = -EOPNOTSUPP;
+  return rv;
+}
+
+int
+vcom_socket_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap)
+{
+  int rv = -EBADF;
+  vcom_socket_main_t *vsm = &vcom_socket_main;
+  uword *p;
+  vcom_socket_t *vsock;
+
+  p = hash_get (vsm->sockidx_by_fd, __fd);
+  if (!p)
+    return -EBADF;
+
+  vsock = pool_elt_at_index (vsm->vsockets, p[0]);
+  if (!vsock)
+    return -ENOTSOCK;
+
+  if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND)
+    return -EINVAL;
+
+  switch (vcom_socket_check_ioctl_cmd (__cmd))
+    {
+      /* Not supported cmd */
+    case 0:
+      rv = -EOPNOTSUPP;
+      break;
+
+      /* cmd not handled by vcom and vppcom */
+    case 1:
+      rv = libc_vioctl (vsock->fd, __cmd, __ap);
+      break;
+
+      /* cmd handled by vcom socket resource */
+    case 2:
+      rv = libc_vioctl (vsock->fd, __cmd, __ap);
+      break;
+
+      /* cmd handled by vppcom */
+    case 3:
+      rv = vppcom_session_ioctl_va (vsock->sid, __cmd, __ap);
+      break;
+
+    default:
+      rv = -EINVAL;
+      break;
+    }
+
+  return rv;
+}
+
 static inline int
 vcom_socket_fds_2_sid_fds (
 			    /* dest */
@@ -1809,17 +1906,41 @@
 
 /* TBD: move it to vppcom */
 int
-vppcom_setsockopt (int __fd, int __level, int __optname,
-		   const void *__optval, socklen_t __optlen)
+vppcom_session_setsockopt (int __sid, int __level, int __optname,
+			   const void *__optval, socklen_t __optlen)
 {
-  /* 1. for socket level options that are NOT socket attributes
-   *    and that has corresponding vpp options set it from vppcom */
-#if 0
-  return 0;
-#endif
+  int rv = -EOPNOTSUPP;
 
-  /* 2. unhandled options */
-  return -ENOPROTOOPT;
+  switch (__level)
+    {
+    case SOL_IPV6:
+      switch (__optname)
+	{
+	case IPV6_V6ONLY:
+	  rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_V6ONLY, 0, 0);
+	  return rv;
+	default:
+	  return rv;
+	}
+      break;
+    case SOL_SOCKET:
+      switch (__optname)
+	{
+	case SO_REUSEADDR:
+	  rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_REUSEADDR, 0, 0);
+	  return rv;
+	case SO_BROADCAST:
+	  rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_BROADCAST, 0, 0);
+	  return rv;
+	default:
+	  return rv;
+	}
+      break;
+    default:
+      return rv;
+    }
+
+  return rv;
 }
 
 int
@@ -1864,10 +1985,36 @@
 
   switch (__level)
     {
+    case SOL_IPV6:
+      switch (__optname)
+	{
+	case IPV6_V6ONLY:
+	  rv = vppcom_session_setsockopt (vsock->sid, __level, __optname,
+					  __optval, __optlen);
+	  return rv;
+	default:
+	  return -EOPNOTSUPP;
+	}
+      break;
+    case SOL_TCP:
+      switch (__optname)
+	{
+	case TCP_NODELAY:
+	  return 0;
+	default:
+	  return -EOPNOTSUPP;
+	}
+      break;
       /* handle options at socket level */
     case SOL_SOCKET:
       switch (__optname)
 	{
+	case SO_REUSEADDR:
+	case SO_BROADCAST:
+	  rv = vppcom_session_setsockopt (vsock->sid, __level, __optname,
+					  __optval, __optlen);
+	  return rv;
+
 	  /*
 	   * 1. for socket level options that are socket attributes,
 	   *    set it from libc_getsockopt
@@ -1878,10 +2025,8 @@
 	   *    return -ENOPROTOOPT */
 	case SO_DEBUG:
 	case SO_DONTROUTE:
-	case SO_BROADCAST:
 	case SO_SNDBUF:
 	case SO_RCVBUF:
-	case SO_REUSEADDR:
 	case SO_REUSEPORT:
 	case SO_KEEPALIVE:
 	case SO_TYPE:
@@ -1942,15 +2087,7 @@
       break;
 
     default:
-      /* 1. handle options that are NOT socket level options,
-       *    but have corresponding vpp otions. */
-      rv = vppcom_setsockopt (vsock->sid, __level, __optname,
-			      __optval, __optlen);
-      return rv;
-#if 0
-      /* 2. unhandled options */
       return -ENOPROTOOPT;
-#endif
     }
 
   return rv;