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
This commit is contained in:
Michael Tuexen 2022-02-09 12:24:41 +01:00
parent 528c764924
commit a35bdd4489
3 changed files with 100 additions and 0 deletions

View File

@ -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(&params->sop_inc.inc6_laddr))
params->sop_inc.inc6_laddr.s6_addr16[1] =
htons(params->sop_inc.inc6_zoneid & 0xffff);
if (IN6_IS_SCOPE_LINKLOCAL(&params->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(
&params->sop_inc.inc6_faddr,
params->sop_inc.inc_lport,
params->sop_inc.inc_fport,
pcbinfo->ipi_hashmask);
else
inpi.hash = INP_PCBHASH(
&params->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)

View File

@ -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_ */

View File

@ -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)