aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2021-09-01 14:04:47 +0000
committerMark Johnston <markj@FreeBSD.org>2021-09-01 14:06:18 +0000
commit457abbb85794ad8b28d11a7cd44260eabdf3114d (patch)
tree25aee778742215b548203111f92d2421b4bdcb68
parentbe8ee77e9edcb0bc8f94cb8695fc7fb49cc0a282 (diff)
downloadsrc-457abbb85794ad8b28d11a7cd44260eabdf3114d.tar.gz
src-457abbb85794ad8b28d11a7cd44260eabdf3114d.zip
sctp: Implement sctp_inpcb_bind_locked()
This will be used by sctp_listen() to avoid dropping locks when performing an implicit bind. No functional change intended. Reviewed by: tuexen MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D31757
-rw-r--r--sys/netinet/sctp_pcb.c53
-rw-r--r--sys/netinet/sctp_pcb.h3
2 files changed, 38 insertions, 18 deletions
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index 12f2d5d7fb76..7aa20b5e14b9 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -2801,14 +2801,19 @@ sctp_remove_laddr(struct sctp_laddr *laddr)
SCTP_DECR_LADDR_COUNT();
}
-/* sctp_ifap is used to bypass normal local address validation checks */
+/*
+ * Bind the socket, with the PCB and global info locks held. Note, if a
+ * socket address is specified, the PCB lock may be dropped and re-acquired.
+ *
+ * sctp_ifap is used to bypass normal local address validation checks.
+ */
int
-sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
+sctp_inpcb_bind_locked(struct sctp_inpcb *inp, struct sockaddr *addr,
struct sctp_ifa *sctp_ifap, struct thread *td)
{
/* bind a ep to a socket address */
struct sctppcbhead *head;
- struct sctp_inpcb *inp, *inp_tmp;
+ struct sctp_inpcb *inp_tmp;
struct inpcb *ip_inp;
int port_reuse_active = 0;
int bindall;
@@ -2821,8 +2826,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
error = 0;
lport = 0;
bindall = 1;
- inp = (struct sctp_inpcb *)so->so_pcb;
- ip_inp = (struct inpcb *)so->so_pcb;
+ ip_inp = &inp->ip_inp.inp;
+
+ SCTP_INP_INFO_WLOCK_ASSERT();
+ SCTP_INP_WLOCK_ASSERT(inp);
+
#ifdef SCTP_DEBUG
if (addr) {
SCTPDBG(SCTP_DEBUG_PCB1, "Bind called port: %d\n",
@@ -2831,8 +2839,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr);
}
#endif
- SCTP_INP_INFO_WLOCK();
- SCTP_INP_WLOCK(inp);
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 0) {
error = EINVAL;
/* already did a bind, subsequent binds NOT allowed ! */
@@ -2925,20 +2931,16 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
vrf_id = inp->def_vrf_id;
if (lport) {
- /* increase our count due to the unlock we do */
- SCTP_INP_INCR_REF(inp);
-
/*
* Did the caller specify a port? if so we must see if an ep
* already has this one bound.
*/
/* got to be root to get at low ports */
- if (ntohs(lport) < IPPORT_RESERVED) {
- if ((error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) {
- SCTP_INP_DECR_REF(inp);
- goto out;
- }
+ if (ntohs(lport) < IPPORT_RESERVED &&
+ (error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) {
+ goto out;
}
+ SCTP_INP_INCR_REF(inp);
SCTP_INP_WUNLOCK(inp);
if (bindall) {
vrf_id = inp->def_vrf_id;
@@ -2962,10 +2964,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
port_reuse_active = 1;
goto continue_anyway;
}
+ SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error);
- goto out_inp_unlocked;
+ goto out;
}
} else {
inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id);
@@ -2988,10 +2991,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
port_reuse_active = 1;
goto continue_anyway;
}
+ SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error);
- goto out_inp_unlocked;
+ goto out;
}
}
continue_anyway:
@@ -3201,8 +3205,21 @@ continue_anyway:
("%s: inp %p is already bound", __func__, inp));
inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND;
out:
+ return (error);
+}
+
+int
+sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
+ struct sctp_ifa *sctp_ifap, struct thread *td)
+{
+ struct sctp_inpcb *inp;
+ int error;
+
+ inp = so->so_pcb;
+ SCTP_INP_INFO_WLOCK();
+ SCTP_INP_WLOCK(inp);
+ error = sctp_inpcb_bind_locked(inp, addr, sctp_ifap, td);
SCTP_INP_WUNLOCK(inp);
-out_inp_unlocked:
SCTP_INP_INFO_WUNLOCK();
return (error);
}
diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h
index c978e8c72b42..e14c9f39356c 100644
--- a/sys/netinet/sctp_pcb.h
+++ b/sys/netinet/sctp_pcb.h
@@ -526,6 +526,9 @@ struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t);
int
sctp_inpcb_bind(struct socket *, struct sockaddr *,
struct sctp_ifa *, struct thread *);
+int
+sctp_inpcb_bind_locked(struct sctp_inpcb *, struct sockaddr *,
+ struct sctp_ifa *, struct thread *);
struct sctp_tcb *
sctp_findassociation_addr(struct mbuf *, int,