git: a35bdd4489b9 - main - tcp: add sysctl interface for setting socket options
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 09 Feb 2022 12:22:36 UTC
The branch main has been updated by tuexen:
URL: https://cgit.FreeBSD.org/src/commit/?id=a35bdd4489b97f14cb46ebd0f5354685e4488af3
commit a35bdd4489b97f14cb46ebd0f5354685e4488af3
Author: Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2022-02-09 11:24:41 +0000
Commit: Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2022-02-09 11:24:41 +0000
tcp: add sysctl interface for setting socket options
This interface allows to set a socket option on a TCP endpoint,
which is specified by its inp_gencnt. This interface will be
used in an upcoming command line tool tcpsso.
Reviewed by: glebius, rrs
Sponsored by: Netflix, Inc.
Differential Revision: https://reviews.freebsd.org/D34138
---
sys/netinet/in_pcb.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++
sys/netinet/in_pcb.h | 12 ++++++++
sys/netinet/tcp_subr.c | 12 ++++++++
3 files changed, 100 insertions(+)
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 97471ae7800c..147e0b88214d 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
+#include <sys/sysctl.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/refcount.h>
@@ -2799,6 +2800,81 @@ in_pcbtoxinpcb(const struct inpcb *inp, struct xinpcb *xi)
xi->inp_ip_minttl = inp->inp_ip_minttl;
}
+int
+sysctl_setsockopt(SYSCTL_HANDLER_ARGS, struct inpcbinfo *pcbinfo,
+ int (*ctloutput_set)(struct inpcb *, struct sockopt *))
+{
+ struct sockopt sopt;
+ struct inpcb_iterator inpi = INP_ALL_ITERATOR(pcbinfo,
+ INPLOOKUP_WLOCKPCB);
+ struct inpcb *inp;
+ struct sockopt_parameters *params;
+ struct socket *so;
+ int error;
+ char buf[1024];
+
+ if (req->oldptr != NULL || req->oldlen != 0)
+ return (EINVAL);
+ if (req->newptr == NULL)
+ return (EPERM);
+ if (req->newlen > sizeof(buf))
+ return (ENOMEM);
+ error = SYSCTL_IN(req, buf, req->newlen);
+ if (error != 0)
+ return (error);
+ if (req->newlen < sizeof(struct sockopt_parameters))
+ return (EINVAL);
+ params = (struct sockopt_parameters *)buf;
+ sopt.sopt_level = params->sop_level;
+ sopt.sopt_name = params->sop_optname;
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_val = params->sop_optval;
+ sopt.sopt_valsize = req->newlen - sizeof(struct sockopt_parameters);
+ sopt.sopt_td = NULL;
+ if (params->sop_inc.inc_flags & INC_ISIPV6) {
+ if (IN6_IS_SCOPE_LINKLOCAL(¶ms->sop_inc.inc6_laddr))
+ params->sop_inc.inc6_laddr.s6_addr16[1] =
+ htons(params->sop_inc.inc6_zoneid & 0xffff);
+ if (IN6_IS_SCOPE_LINKLOCAL(¶ms->sop_inc.inc6_faddr))
+ params->sop_inc.inc6_faddr.s6_addr16[1] =
+ htons(params->sop_inc.inc6_zoneid & 0xffff);
+ }
+ if (params->sop_inc.inc_lport != htons(0)) {
+ if (params->sop_inc.inc_fport == htons(0))
+ inpi.hash = INP_PCBHASH_WILD(params->sop_inc.inc_lport,
+ pcbinfo->ipi_hashmask);
+ else
+ if (params->sop_inc.inc_flags & INC_ISIPV6)
+ inpi.hash = INP6_PCBHASH(
+ ¶ms->sop_inc.inc6_faddr,
+ params->sop_inc.inc_lport,
+ params->sop_inc.inc_fport,
+ pcbinfo->ipi_hashmask);
+ else
+ inpi.hash = INP_PCBHASH(
+ ¶ms->sop_inc.inc_faddr,
+ params->sop_inc.inc_lport,
+ params->sop_inc.inc_fport,
+ pcbinfo->ipi_hashmask);
+ }
+ while ((inp = inp_next(&inpi)) != NULL)
+ if (inp->inp_gencnt == params->sop_id) {
+ if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+ INP_WUNLOCK(inp);
+ return (ECONNRESET);
+ }
+ so = inp->inp_socket;
+ KASSERT(so != NULL, ("inp_socket == NULL"));
+ soref(so);
+ error = (*ctloutput_set)(inp, &sopt);
+ sorele(so);
+ break;
+ }
+ if (inp == NULL)
+ error = ESRCH;
+ return (error);
+}
+
#ifdef DDB
static void
db_print_indent(int indent)
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 49b891e33b15..c6a8448b8791 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -52,6 +52,7 @@
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/smr.h>
+#include <sys/sysctl.h>
#include <net/vnet.h>
#include <vm/uma.h>
#endif
@@ -368,7 +369,18 @@ struct xinpgen {
so_gen_t xig_sogen; /* socket generation count this time */
uint64_t _xig_spare64[4];
} __aligned(8);
+
+struct sockopt_parameters {
+ struct in_conninfo sop_inc;
+ uint64_t sop_id;
+ int sop_level;
+ int sop_optname;
+ char sop_optval[];
+};
+
#ifdef _KERNEL
+int sysctl_setsockopt(SYSCTL_HANDLER_ARGS, struct inpcbinfo *pcbinfo,
+ int (*ctloutput_set)(struct inpcb *, struct sockopt *));
void in_pcbtoxinpcb(const struct inpcb *, struct xinpcb *);
#endif
#endif /* _SYS_SOCKETVAR_H_ */
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 1cd53db3bf99..298ae38d5b27 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -3834,6 +3834,18 @@ SYSCTL_PROC(_net_inet_tcp, TCPCTL_DROP, drop,
CTLFLAG_NEEDGIANT, NULL, 0, sysctl_drop, "",
"Drop TCP connection");
+static int
+tcp_sysctl_setsockopt(SYSCTL_HANDLER_ARGS)
+{
+ return (sysctl_setsockopt(oidp, arg1, arg2, req, &V_tcbinfo,
+ &tcp_ctloutput_set));
+}
+
+SYSCTL_PROC(_net_inet_tcp, OID_AUTO, setsockopt,
+ CTLFLAG_VNET | CTLTYPE_STRUCT | CTLFLAG_WR | CTLFLAG_SKIP |
+ CTLFLAG_MPSAFE, NULL, 0, tcp_sysctl_setsockopt, "",
+ "Set socket option for TCP endpoint");
+
#ifdef KERN_TLS
static int
sysctl_switch_tls(SYSCTL_HANDLER_ARGS)