socket: Move SO_SETFIB handling to protocol layers

In particular, we store a FIB number in both struct socket and in struct
inpcb.  When updating the FIB number with setsockopt(SO_SETFIB), make
the update atomic.  This is required to support the new bind_all_fibs
mode, since in that mode changing the FIB of a bound socket is not
permitted.

This requires a bit more code, but avoids a layering violation in
sosetopt(), where we hard-code the list of protocol families that
implement SO_SETFIB.

Reviewed by:	glebius
MFC after:	2 weeks
Sponsored by:	Klara, Inc.
Sponsored by:	Stormshield
Differential Revision:	https://reviews.freebsd.org/D48666
This commit is contained in:
Mark Johnston 2025-02-06 14:16:21 +00:00
parent 7034563f8e
commit caccbaef8e
7 changed files with 76 additions and 33 deletions

View file

@ -3699,6 +3699,19 @@ sorflush(struct socket *so)
}
int
sosetfib(struct socket *so, int fibnum)
{
if (fibnum < 0 || fibnum >= rt_numfibs)
return (EINVAL);
SOCK_LOCK(so);
so->so_fibnum = fibnum;
SOCK_UNLOCK(so);
return (0);
}
#ifdef SOCKET_HHOOK
/*
* Wrapper for Socket established helper hook.
@ -3847,21 +3860,7 @@ sosetopt(struct socket *so, struct sockopt *sopt)
break;
case SO_SETFIB:
error = sooptcopyin(sopt, &optval, sizeof optval,
sizeof optval);
if (error)
goto bad;
if (optval < 0 || optval >= rt_numfibs) {
error = EINVAL;
goto bad;
}
if (((so->so_proto->pr_domain->dom_family == PF_INET) ||
(so->so_proto->pr_domain->dom_family == PF_INET6) ||
(so->so_proto->pr_domain->dom_family == PF_ROUTE)))
so->so_fibnum = optval;
else
so->so_fibnum = 0;
error = so->so_proto->pr_ctloutput(so, sopt);
break;
case SO_USER_COOKIE:

View file

@ -423,6 +423,30 @@ rts_attach(struct socket *so, int proto, struct thread *td)
return (0);
}
static int
rts_ctloutput(struct socket *so, struct sockopt *sopt)
{
int error, optval;
error = ENOPROTOOPT;
if (sopt->sopt_dir == SOPT_SET) {
switch (sopt->sopt_level) {
case SOL_SOCKET:
switch (sopt->sopt_name) {
case SO_SETFIB:
error = sooptcopyin(sopt, &optval,
sizeof(optval), sizeof(optval));
if (error != 0)
break;
error = sosetfib(so, optval);
break;
}
break;
}
}
return (error);
}
static void
rts_detach(struct socket *so)
{
@ -2702,6 +2726,7 @@ static struct protosw routesw = {
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_abort = rts_close,
.pr_attach = rts_attach,
.pr_ctloutput = rts_ctloutput,
.pr_detach = rts_detach,
.pr_send = rts_send,
.pr_shutdown = rts_shutdown,

View file

@ -1094,10 +1094,22 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
sopt->sopt_dir == SOPT_SET) {
switch (sopt->sopt_name) {
case SO_SETFIB:
error = sooptcopyin(sopt, &optval,
sizeof(optval), sizeof(optval));
if (error != 0)
break;
INP_WLOCK(inp);
inp->inp_inc.inc_fibnum = so->so_fibnum;
if ((inp->inp_flags & INP_BOUNDFIB) != 0 &&
optval != so->so_fibnum) {
INP_WUNLOCK(inp);
error = EISCONN;
break;
}
error = sosetfib(inp->inp_socket, optval);
if (error == 0)
inp->inp_inc.inc_fibnum = optval;
INP_WUNLOCK(inp);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT

View file

@ -633,13 +633,10 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
int error, optval;
if (sopt->sopt_level != IPPROTO_IP) {
if ((sopt->sopt_level == SOL_SOCKET) &&
(sopt->sopt_name == SO_SETFIB)) {
INP_WLOCK(inp);
inp->inp_inc.inc_fibnum = so->so_fibnum;
INP_WUNLOCK(inp);
return (0);
}
if (sopt->sopt_dir == SOPT_SET &&
sopt->sopt_level == SOL_SOCKET &&
sopt->sopt_name == SO_SETFIB)
return (ip_ctloutput(so, sopt));
return (EINVAL);
}

View file

@ -1648,10 +1648,22 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
sopt->sopt_dir == SOPT_SET) {
switch (sopt->sopt_name) {
case SO_SETFIB:
error = sooptcopyin(sopt, &optval,
sizeof(optval), sizeof(optval));
if (error != 0)
break;
INP_WLOCK(inp);
inp->inp_inc.inc_fibnum = so->so_fibnum;
if ((inp->inp_flags & INP_BOUNDFIB) != 0 &&
optval != so->so_fibnum) {
INP_WUNLOCK(inp);
error = EISCONN;
break;
}
error = sosetfib(inp->inp_socket, optval);
if (error == 0)
inp->inp_inc.inc_fibnum = optval;
INP_WUNLOCK(inp);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT

View file

@ -576,13 +576,10 @@ rip6_ctloutput(struct socket *so, struct sockopt *sopt)
*/
return (icmp6_ctloutput(so, sopt));
else if (sopt->sopt_level != IPPROTO_IPV6) {
if (sopt->sopt_level == SOL_SOCKET &&
sopt->sopt_name == SO_SETFIB) {
INP_WLOCK(inp);
inp->inp_inc.inc_fibnum = so->so_fibnum;
INP_WUNLOCK(inp);
return (0);
}
if (sopt->sopt_dir == SOPT_SET &&
sopt->sopt_level == SOL_SOCKET &&
sopt->sopt_name == SO_SETFIB)
return (ip6_ctloutput(so, sopt));
return (EINVAL);
}

View file

@ -552,6 +552,7 @@ int sosend_dgram(struct socket *so, struct sockaddr *addr,
int sosend_generic(struct socket *so, struct sockaddr *addr,
struct uio *uio, struct mbuf *top, struct mbuf *control,
int flags, struct thread *td);
int sosetfib(struct socket *so, int fibnum);
int soshutdown(struct socket *so, enum shutdown_how);
void soupcall_clear(struct socket *, sb_which);
void soupcall_set(struct socket *, sb_which, so_upcall_t, void *);