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.c b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.c
index 6245c5d..d5b3e12 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.c
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.c
@@ -426,6 +426,61 @@
return rv;
}
+int
+vcom_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap)
+{
+ if (vcom_init () != 0)
+ {
+ return -1;
+ }
+
+ return vcom_socket_ioctl_va (__fd, __cmd, __ap);
+}
+
+int
+vcom_ioctl (int __fd, unsigned long int __cmd, ...)
+{
+ int rv = -1;
+ va_list ap;
+
+ if (is_vcom_socket_fd (__fd))
+ {
+ va_start (ap, __cmd);
+ rv = vcom_ioctl_va (__fd, __cmd, ap);
+ va_end (ap);
+ }
+ return rv;
+}
+
+int
+ioctl (int __fd, unsigned long int __cmd, ...)
+{
+ int rv;
+ va_list ap;
+ pid_t pid = getpid ();
+
+ va_start (ap, __cmd);
+ if (is_vcom_socket_fd (__fd))
+ {
+ rv = vcom_ioctl_va (__fd, __cmd, ap);
+ if (VCOM_DEBUG > 0)
+ fprintf (stderr,
+ "[%d] ioctl: "
+ "'%04d'='%04d', '%04ld'\n", pid, rv, __fd, __cmd);
+ if (rv < 0)
+ {
+ errno = -rv;
+ rv = -1;
+ }
+ goto out;
+ }
+ rv = libc_vioctl (__fd, __cmd, ap);
+
+out:
+ va_end (ap);
+ return rv;
+}
+
/*
* Check the first NFDS descriptors each in READFDS (if not NULL) for
* read readiness, in WRITEFDS (if not NULL) for write readiness,
diff --git a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.h b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.h
index f6ee8a2..bedeef8 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.h
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom.h
@@ -56,6 +56,11 @@
extern int vcom_fcntl (int __fd, int __cmd, ...);
/*
+ * VPP implementation of glibc APIs ioctl
+ */
+extern int vcom_ioctl (int __fd, unsigned long int __cmd, ...);
+
+/*
* vpp implementation of glibc APIs from <sys/select.h>
*/
extern int
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;
diff --git a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.h b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.h
index 7170e20..ef57646 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.h
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket.h
@@ -347,6 +347,8 @@
int vcom_socket_fcntl_va (int __fd, int __cmd, va_list __ap);
+int vcom_socket_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap);
+
int
vcom_socket_select (int vcom_nfds, fd_set * __restrict vcom_readfds,
fd_set * __restrict vcom_writefds,
diff --git a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.c b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.c
index 74a0dcf..087cd6b 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.c
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.c
@@ -555,6 +555,27 @@
return rc;
}
+DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE int
+libc_vioctl (int fd, int cmd, va_list ap)
+{
+ long int args[4];
+ int rc;
+ int i;
+
+ swrap_bind_symbol_libc (ioctl);
+
+ for (i = 0; i < 4; i++)
+ {
+ args[i] = va_arg (ap, long int);
+ }
+
+ rc = swrap.libc.symbols._libc_ioctl.f (fd,
+ cmd,
+ args[0], args[1], args[2], args[3]);
+
+ return rc;
+}
+
int
libc_getpeername (int sockfd, struct sockaddr *addr, socklen_t * addrlen)
{
diff --git a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.h b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.h
index ef3ece0..49ee7ee 100644
--- a/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.h
+++ b/extras/vcl-ldpreload/src/libvcl-ldpreload/vcom_socket_wrapper.h
@@ -131,6 +131,9 @@
DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE int
libc_vfcntl (int fd, int cmd, va_list ap);
+DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE int
+libc_vioctl (int fd, int cmd, va_list ap);
+
int libc_getpeername (int sockfd, struct sockaddr *addr, socklen_t * addrlen);
int libc_getsockname (int sockfd, struct sockaddr *addr, socklen_t * addrlen);