aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linux/linux_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linux/linux_socket.c')
-rw-r--r--sys/compat/linux/linux_socket.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index b1a483ce611c..023be1e6b885 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -594,10 +594,34 @@ linux_to_bsd_tcp_sockopt(int opt)
return (-2);
case LINUX_TCP_MD5SIG:
return (TCP_MD5SIG);
+ case LINUX_TCP_USER_TIMEOUT:
+ return (TCP_MAXUNACKTIME);
}
return (-1);
}
+static u_int
+linux_to_bsd_tcp_user_timeout(l_uint linux_timeout)
+{
+
+ /*
+ * Linux exposes TCP_USER_TIMEOUT in milliseconds while
+ * TCP_MAXUNACKTIME uses whole seconds. Round up partial
+ * seconds so a non-zero Linux timeout never becomes zero.
+ */
+ return (howmany(linux_timeout, 1000U));
+}
+
+static l_uint
+bsd_to_linux_tcp_user_timeout(u_int bsd_timeout)
+{
+
+ if (bsd_timeout > UINT_MAX / 1000U)
+ return (UINT_MAX);
+
+ return (bsd_timeout * 1000U);
+}
+
static int
linux_to_bsd_msg_flags(int flags)
{
@@ -2057,8 +2081,10 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
struct proc *p = td->td_proc;
struct linux_pemuldata *pem;
l_timeval linux_tv;
+ l_uint linux_timeout;
struct sockaddr *sa;
struct timeval tv;
+ u_int bsd_timeout;
socklen_t len;
int error, level, name, val;
@@ -2130,6 +2156,24 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
break;
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(args->optname);
+ switch (name) {
+ case TCP_MAXUNACKTIME:
+ if (args->optlen < sizeof(linux_timeout))
+ return (EINVAL);
+
+ error = copyin(PTRIN(args->optval), &linux_timeout,
+ sizeof(linux_timeout));
+ if (error != 0)
+ return (error);
+
+ bsd_timeout = linux_to_bsd_tcp_user_timeout(
+ linux_timeout);
+ return (kern_setsockopt(td, args->s, level, name,
+ &bsd_timeout, UIO_SYSSPACE,
+ sizeof(bsd_timeout)));
+ default:
+ break;
+ }
break;
case SOL_NETLINK:
name = args->optname;
@@ -2279,10 +2323,12 @@ linux_getsockopt_so_linger(struct thread *td,
int
linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
{
+ l_uint linux_timeout;
l_timeval linux_tv;
struct timeval tv;
socklen_t tv_len, xulen, len;
struct sockaddr *sa;
+ u_int bsd_timeout;
struct xucred xu;
struct l_ucred lxu;
int error, level, name, newval;
@@ -2373,6 +2419,21 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
break;
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(args->optname);
+ switch (name) {
+ case TCP_MAXUNACKTIME:
+ len = sizeof(bsd_timeout);
+ error = kern_getsockopt(td, args->s, level, name,
+ &bsd_timeout, UIO_SYSSPACE, &len);
+ if (error != 0)
+ return (error);
+
+ linux_timeout = bsd_to_linux_tcp_user_timeout(
+ bsd_timeout);
+ return (linux_sockopt_copyout(td, &linux_timeout,
+ sizeof(linux_timeout), args));
+ default:
+ break;
+ }
break;
default:
name = -1;