Re: git: 01f8ce83242d - main - inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()
- Reply: Mark Johnston : "Re: git: 01f8ce83242d - main - inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()"
- In reply to: Mark Johnston : "git: 01f8ce83242d - main - inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 05 Dec 2024 16:35:23 UTC
On Thu, 5 Dec 2024 15:20:48 GMT Mark Johnston <markj@FreeBSD.org> wrote: This commit breaks buildkernel: [...] --- in6_pcb.o --- /usr/src/sys/netinet6/in6_pcb.c:301:20: error: unused variable 'pcbinfo' [-Werror,-Wunused-variable] 301 | struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; > The branch main has been updated by markj: > > URL: https://cgit.FreeBSD.org/src/commit/?id=01f8ce83242d7a8e599cf6a78b6277161d79edd4 > > commit 01f8ce83242d7a8e599cf6a78b6277161d79edd4 > Author: Mark Johnston <markj@FreeBSD.org> > AuthorDate: 2024-12-05 15:00:13 +0000 > Commit: Mark Johnston <markj@FreeBSD.org> > CommitDate: 2024-12-05 15:19:57 +0000 > > inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup() > > A large portion of these functions just determines whether the inpcb can > bind to the address/port. This portion has no side effects, so is a > good candidate to move into its own helper function. This patch does > so, making the callers less complicated and reducing indentation. > > While moving this code, also make some changes: > - Load socket options (SO_REUSEADDR etc.) only once. There is nothing > preventing another thread from toggling the socket options, so make > this function easier to reason about by avoiding races. > - When checking whether the bind address is an interface address, make a > separate sockaddr rather than temporarily modifying the one passed to > in_pcbbind(). > > Reviewed by: ae, glebius > MFC after: 1 month > Sponsored by: Klara, Inc. > Sponsored by: Stormshield > Differential Revision: https://reviews.freebsd.org/D47590 > --- > sys/netinet/in_pcb.c | 172 ++++++++++++++++++-------------- > sys/netinet6/in6_pcb.c | 262 +++++++++++++++++++++++++++---------------------- > 2 files changed, 242 insertions(+), 192 deletions(-) > > diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c > index 7c881817ac6e..94fb406bcd2b 100644 > --- a/sys/netinet/in_pcb.c > +++ b/sys/netinet/in_pcb.c > @@ -861,6 +861,93 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *laddrp, u_short > *lportp, #endif /* INET || INET6 */ > > #ifdef INET > +/* > + * Determine whether the inpcb can be bound to the specified address/port tuple. > + */ > +static int > +in_pcbbind_avail(struct inpcb *inp, const struct in_addr laddr, > + const u_short lport, int sooptions, int lookupflags, struct ucred *cred) > +{ > + int reuseport, reuseport_lb; > + > + INP_LOCK_ASSERT(inp); > + INP_HASH_LOCK_ASSERT(inp->inp_pcbinfo); > + > + reuseport = (sooptions & SO_REUSEPORT); > + reuseport_lb = (sooptions & SO_REUSEPORT_LB); > + > + if (IN_MULTICAST(ntohl(laddr.s_addr))) { > + /* > + * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; > + * allow complete duplication of binding if > + * SO_REUSEPORT is set, or if SO_REUSEADDR is set > + * and a multicast address is bound on both > + * new and duplicated sockets. > + */ > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT)) != 0) > + reuseport = SO_REUSEADDR | SO_REUSEPORT; > + /* > + * XXX: How to deal with SO_REUSEPORT_LB here? > + * Treat same as SO_REUSEPORT for now. > + */ > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT_LB)) != 0) > + reuseport_lb = SO_REUSEADDR | SO_REUSEPORT_LB; > + } else if (!in_nullhost(laddr)) { > + struct sockaddr_in sin; > + > + memset(&sin, 0, sizeof(sin)); > + sin.sin_family = AF_INET; > + sin.sin_len = sizeof(sin); > + sin.sin_addr = laddr; > + > + /* > + * Is the address a local IP address? > + * If INP_BINDANY is set, then the socket may be bound > + * to any endpoint address, local or not. > + */ > + if ((inp->inp_flags & INP_BINDANY) == 0 && > + ifa_ifwithaddr_check((const struct sockaddr *)&sin) == 0) > + return (EADDRNOTAVAIL); > + } > + > + if (lport != 0) { > + struct inpcb *t; > + > + if (ntohs(lport) <= V_ipport_reservedhigh && > + ntohs(lport) >= V_ipport_reservedlow && > + priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT)) > + return (EACCES); > + > + if (!IN_MULTICAST(ntohl(laddr.s_addr)) && > + priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) != 0) { > + t = in_pcblookup_local(inp->inp_pcbinfo, laddr, lport, > + INPLOOKUP_WILDCARD, cred); > + if (t != NULL && > + (inp->inp_socket->so_type != SOCK_STREAM || > + in_nullhost(t->inp_faddr)) && > + (!in_nullhost(laddr) || > + !in_nullhost(t->inp_laddr) || > + (t->inp_socket->so_options & SO_REUSEPORT) || > + (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) && > + (inp->inp_cred->cr_uid != t->inp_cred->cr_uid)) > + return (EADDRINUSE); > + } > + t = in_pcblookup_local(inp->inp_pcbinfo, laddr, lport, > + lookupflags, cred); > + if (t != NULL && ((reuseport | reuseport_lb) & > + t->inp_socket->so_options) == 0) { > +#ifdef INET6 > + if (!in_nullhost(laddr) || > + !in_nullhost(t->inp_laddr) || > + (inp->inp_vflag & INP_IPV6PROTO) == 0 || > + (t->inp_vflag & INP_IPV6PROTO) == 0) > +#endif > + return (EADDRINUSE); > + } > + } > + return (0); > +} > + > /* > * Set up a bind operation on a PCB, performing port allocation > * as required, but do not actually modify the PCB. Callers can > @@ -878,15 +965,9 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, > in_addr_t *laddrp, struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; > struct in_addr laddr; > u_short lport = 0; > - int lookupflags = 0, reuseport = (so->so_options & SO_REUSEPORT); > + int lookupflags, sooptions; > int error; > > - /* > - * XXX: Maybe we could let SO_REUSEPORT_LB set SO_REUSEPORT bit here > - * so that we don't have to add to the (already messy) code below. > - */ > - int reuseport_lb = (so->so_options & SO_REUSEPORT_LB); > - > /* > * No state changes, so read locks are sufficient here. > */ > @@ -896,7 +977,10 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, > in_addr_t *laddrp, laddr.s_addr = *laddrp; > if (sin != NULL && laddr.s_addr != INADDR_ANY) > return (EINVAL); > - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT|SO_REUSEPORT_LB)) == 0) > + > + lookupflags = 0; > + sooptions = atomic_load_int(&so->so_options); > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT | SO_REUSEPORT_LB)) == 0) > lookupflags = INPLOOKUP_WILDCARD; > if (sin == NULL) { > if ((error = prison_local_ip4(cred, &laddr)) != 0) > @@ -918,73 +1002,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, > in_addr_t *laddrp, } > laddr = sin->sin_addr; > > - /* NB: lport is left as 0 if the port isn't being changed. */ > - if (IN_MULTICAST(ntohl(laddr.s_addr))) { > - /* > - * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; > - * allow complete duplication of binding if > - * SO_REUSEPORT is set, or if SO_REUSEADDR is set > - * and a multicast address is bound on both > - * new and duplicated sockets. > - */ > - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0) > - reuseport = SO_REUSEADDR|SO_REUSEPORT; > - /* > - * XXX: How to deal with SO_REUSEPORT_LB here? > - * Treat same as SO_REUSEPORT for now. > - */ > - if ((so->so_options & > - (SO_REUSEADDR|SO_REUSEPORT_LB)) != 0) > - reuseport_lb = SO_REUSEADDR|SO_REUSEPORT_LB; > - } else if (!in_nullhost(laddr)) { > - sin->sin_port = 0; /* yech... */ > - bzero(&sin->sin_zero, sizeof(sin->sin_zero)); > - /* > - * Is the address a local IP address? > - * If INP_BINDANY is set, then the socket may be bound > - * to any endpoint address, local or not. > - */ > - if ((inp->inp_flags & INP_BINDANY) == 0 && > - ifa_ifwithaddr_check( > - (const struct sockaddr *)sin) == 0) > - return (EADDRNOTAVAIL); > - } > - if (lport) { > - struct inpcb *t; > - > - if (ntohs(lport) <= V_ipport_reservedhigh && > - ntohs(lport) >= V_ipport_reservedlow && > - priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT)) > - return (EACCES); > - > - if (!IN_MULTICAST(ntohl(laddr.s_addr)) && > - priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) != > 0) { > - t = in_pcblookup_local(pcbinfo, laddr, lport, > - INPLOOKUP_WILDCARD, cred); > - if (t != NULL && > - (so->so_type != SOCK_STREAM || > - in_nullhost(t->inp_faddr)) && > - (!in_nullhost(laddr) || > - !in_nullhost(t->inp_laddr) || > - (t->inp_socket->so_options & SO_REUSEPORT) || > - (t->inp_socket->so_options & SO_REUSEPORT_LB) == > 0) && > - (inp->inp_cred->cr_uid != > - t->inp_cred->cr_uid)) > - return (EADDRINUSE); > - } > - t = in_pcblookup_local(pcbinfo, laddr, lport, > - lookupflags, cred); > - if (t != NULL && ((reuseport | reuseport_lb) & > - t->inp_socket->so_options) == 0) { > -#ifdef INET6 > - if (!in_nullhost(laddr) || > - !in_nullhost(t->inp_laddr) || > - (inp->inp_vflag & INP_IPV6PROTO) == 0 || > - (t->inp_vflag & INP_IPV6PROTO) == 0) > -#endif > - return (EADDRINUSE); > - } > - } > + /* See if this address/port combo is available. */ > + error = in_pcbbind_avail(inp, laddr, lport, sooptions, > + lookupflags, cred); > + if (error != 0) > + return (error); > } > if (*lportp != 0) > lport = *lportp; > diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c > index 639ec68e19e2..6b5a9d3706be 100644 > --- a/sys/netinet6/in6_pcb.c > +++ b/sys/netinet6/in6_pcb.c > @@ -160,27 +160,157 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct > ucred *cred) return (0); > } > > +/* > + * Determine whether the inpcb can be bound to the specified address/port tuple. > + */ > +static int > +in6_pcbbind_avail(struct inpcb *inp, const struct sockaddr_in6 *sin6, > + int sooptions, int lookupflags, struct ucred *cred) > +{ > + const struct in6_addr *laddr; > + int reuseport, reuseport_lb; > + u_short lport; > + > + INP_LOCK_ASSERT(inp); > + INP_HASH_LOCK_ASSERT(inp->inp_pcbinfo); > + > + laddr = &sin6->sin6_addr; > + lport = sin6->sin6_port; > + > + reuseport = (sooptions & SO_REUSEPORT); > + reuseport_lb = (sooptions & SO_REUSEPORT_LB); > + > + if (IN6_IS_ADDR_MULTICAST(laddr)) { > + /* > + * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; > + * allow compepte duplication of binding if > + * SO_REUSEPORT is set, or if SO_REUSEADDR is set > + * and a multicast address is bound on both > + * new and duplicated sockets. > + */ > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT)) != 0) > + reuseport = SO_REUSEADDR | SO_REUSEPORT; > + /* > + * XXX: How to deal with SO_REUSEPORT_LB here? > + * Treat same as SO_REUSEPORT for now. > + */ > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT_LB)) != 0) > + reuseport_lb = SO_REUSEADDR | SO_REUSEPORT_LB; > + } else if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) { > + struct sockaddr_in6 sin6; > + struct epoch_tracker et; > + struct ifaddr *ifa; > + > + memset(&sin6, 0, sizeof(sin6)); > + sin6.sin6_family = AF_INET6; > + sin6.sin6_len = sizeof(sin6); > + sin6.sin6_addr = *laddr; > + > + NET_EPOCH_ENTER(et); > + if ((ifa = ifa_ifwithaddr((const struct sockaddr *)&sin6)) == > + NULL && (inp->inp_flags & INP_BINDANY) == 0) { > + NET_EPOCH_EXIT(et); > + return (EADDRNOTAVAIL); > + } > + > + /* > + * XXX: bind to an anycast address might accidentally > + * cause sending a packet with anycast source address. > + * We should allow to bind to a deprecated address, since > + * the application dares to use it. > + */ > + if (ifa != NULL && > + ((struct in6_ifaddr *)ifa)->ia6_flags & > + (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY | IN6_IFF_DETACHED)) { > + NET_EPOCH_EXIT(et); > + return (EADDRNOTAVAIL); > + } > + NET_EPOCH_EXIT(et); > + } > + > + if (lport != 0) { > + struct inpcb *t; > + > + if (ntohs(lport) <= V_ipport_reservedhigh && > + ntohs(lport) >= V_ipport_reservedlow && > + priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT)) > + return (EACCES); > + > + if (!IN6_IS_ADDR_MULTICAST(laddr) && > + priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) != > + 0) { > + t = in6_pcblookup_local(inp->inp_pcbinfo, laddr, lport, > + INPLOOKUP_WILDCARD, cred); > + if (t != NULL && > + (inp->inp_socket->so_type != SOCK_STREAM || > + IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && > + (!IN6_IS_ADDR_UNSPECIFIED(laddr) || > + !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || > + (t->inp_socket->so_options & SO_REUSEPORT) || > + (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) && > + (inp->inp_cred->cr_uid != t->inp_cred->cr_uid)) > + return (EADDRINUSE); > + > +#ifdef INET > + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && > + IN6_IS_ADDR_UNSPECIFIED(laddr)) { > + struct sockaddr_in sin; > + > + in6_sin6_2_sin(&sin, sin6); > + t = in_pcblookup_local(inp->inp_pcbinfo, > + sin.sin_addr, lport, INPLOOKUP_WILDCARD, > + cred); > + if (t != NULL && > + (inp->inp_socket->so_type != SOCK_STREAM || > + in_nullhost(t->inp_faddr)) && > + (inp->inp_cred->cr_uid != > + t->inp_cred->cr_uid)) > + return (EADDRINUSE); > + } > +#endif > + } > + t = in6_pcblookup_local(inp->inp_pcbinfo, laddr, lport, > + lookupflags, cred); > + if (t != NULL && ((reuseport | reuseport_lb) & > + t->inp_socket->so_options) == 0) > + return (EADDRINUSE); > +#ifdef INET > + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && > + IN6_IS_ADDR_UNSPECIFIED(laddr)) { > + struct sockaddr_in sin; > + > + in6_sin6_2_sin(&sin, sin6); > + t = in_pcblookup_local(inp->inp_pcbinfo, sin.sin_addr, > + lport, lookupflags, cred); > + if (t != NULL && ((reuseport | reuseport_lb) & > + t->inp_socket->so_options) == 0 && > + (!in_nullhost(t->inp_laddr) || > + (t->inp_vflag & INP_IPV6PROTO) != 0)) { > + return (EADDRINUSE); > + } > + } > +#endif > + } > + return (0); > +} > + > int > in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred) > { > struct socket *so = inp->inp_socket; > struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; > u_short lport = 0; > - int error, lookupflags = 0; > - int reuseport = (so->so_options & SO_REUSEPORT); > - > - /* > - * XXX: Maybe we could let SO_REUSEPORT_LB set SO_REUSEPORT bit here > - * so that we don't have to add to the (already messy) code below. > - */ > - int reuseport_lb = (so->so_options & SO_REUSEPORT_LB); > + int error, lookupflags, sooptions; > > INP_WLOCK_ASSERT(inp); > INP_HASH_WLOCK_ASSERT(pcbinfo); > > if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) > return (EINVAL); > - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT|SO_REUSEPORT_LB)) == 0) > + > + lookupflags = 0; > + sooptions = atomic_load_int(&so->so_options); > + if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT | SO_REUSEPORT_LB)) == 0) > lookupflags = INPLOOKUP_WILDCARD; > if (sin6 == NULL) { > if ((error = prison_local_ip6(cred, &inp->in6p_laddr, > @@ -199,115 +329,13 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, struct > ucred *cred) ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0) > return (error); > > - lport = sin6->sin6_port; > - if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { > - /* > - * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; > - * allow compepte duplication of binding if > - * SO_REUSEPORT is set, or if SO_REUSEADDR is set > - * and a multicast address is bound on both > - * new and duplicated sockets. > - */ > - if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0) > - reuseport = SO_REUSEADDR|SO_REUSEPORT; > - /* > - * XXX: How to deal with SO_REUSEPORT_LB here? > - * Treat same as SO_REUSEPORT for now. > - */ > - if ((so->so_options & > - (SO_REUSEADDR|SO_REUSEPORT_LB)) != 0) > - reuseport_lb = SO_REUSEADDR|SO_REUSEPORT_LB; > - } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { > - struct epoch_tracker et; > - struct ifaddr *ifa; > - > - sin6->sin6_port = 0; /* yech... */ > - NET_EPOCH_ENTER(et); > - if ((ifa = ifa_ifwithaddr((struct sockaddr *)sin6)) == > - NULL && > - (inp->inp_flags & INP_BINDANY) == 0) { > - NET_EPOCH_EXIT(et); > - return (EADDRNOTAVAIL); > - } > - > - /* > - * XXX: bind to an anycast address might accidentally > - * cause sending a packet with anycast source address. > - * We should allow to bind to a deprecated address, since > - * the application dares to use it. > - */ > - if (ifa != NULL && > - ((struct in6_ifaddr *)ifa)->ia6_flags & > - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { > - NET_EPOCH_EXIT(et); > - return (EADDRNOTAVAIL); > - } > - NET_EPOCH_EXIT(et); > - } > - if (lport) { > - struct inpcb *t; > - > - if (ntohs(lport) <= V_ipport_reservedhigh && > - ntohs(lport) >= V_ipport_reservedlow && > - priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT)) > - return (EACCES); > - > - if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && > - priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) != > 0) { > - t = in6_pcblookup_local(pcbinfo, > - &sin6->sin6_addr, lport, > - INPLOOKUP_WILDCARD, cred); > - if (t != NULL && > - (so->so_type != SOCK_STREAM || > - IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && > - (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || > - !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || > - (t->inp_socket->so_options & SO_REUSEPORT) || > - (t->inp_socket->so_options & SO_REUSEPORT_LB) == > 0) && > - (inp->inp_cred->cr_uid != > - t->inp_cred->cr_uid)) > - return (EADDRINUSE); > - > -#ifdef INET > - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && > - IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { > - struct sockaddr_in sin; > - > - in6_sin6_2_sin(&sin, sin6); > - t = in_pcblookup_local(pcbinfo, > - sin.sin_addr, lport, > - INPLOOKUP_WILDCARD, cred); > - if (t != NULL && > - (so->so_type != SOCK_STREAM || > - in_nullhost(t->inp_faddr)) && > - (inp->inp_cred->cr_uid != > - t->inp_cred->cr_uid)) > - return (EADDRINUSE); > - } > -#endif > - } > - t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, > - lport, lookupflags, cred); > - if (t != NULL && ((reuseport | reuseport_lb) & > - t->inp_socket->so_options) == 0) > - return (EADDRINUSE); > -#ifdef INET > - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && > - IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { > - struct sockaddr_in sin; > + /* See if this address/port combo is available. */ > + error = in6_pcbbind_avail(inp, sin6, sooptions, lookupflags, > + cred); > + if (error != 0) > + return (error); > > - in6_sin6_2_sin(&sin, sin6); > - t = in_pcblookup_local(pcbinfo, sin.sin_addr, > - lport, lookupflags, cred); > - if (t != NULL && ((reuseport | reuseport_lb) & > - t->inp_socket->so_options) == 0 && > - (!in_nullhost(t->inp_laddr) || > - (t->inp_vflag & INP_IPV6PROTO) != 0)) { > - return (EADDRINUSE); > - } > - } > -#endif > - } > + lport = sin6->sin6_port; > inp->in6p_laddr = sin6->sin6_addr; > } > if (lport == 0) { >