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

(cherry picked from commit caccbaef8e263b1d769e7bcac1c4617bdc12d484)
This commit is contained in:
Mark Johnston 2025-02-06 14:16:21 +00:00
parent d2ae0070a1
commit a55bde2328
7 changed files with 76 additions and 33 deletions

View file

@ -3698,6 +3698,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);
}
/*
* Wrapper for Socket established helper hook.
* Parameters: socket, context of the hook point, hook id.
@ -3844,21 +3857,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

@ -425,6 +425,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)
{
@ -2687,6 +2711,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

@ -1100,10 +1100,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

@ -635,13 +635,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

@ -1658,10 +1658,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

@ -578,13 +578,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

@ -555,6 +555,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, int how);
void soupcall_clear(struct socket *, sb_which);
void soupcall_set(struct socket *, sb_which, so_upcall_t, void *);