mirror of
https://git.FreeBSD.org/src.git
synced 2024-11-27 08:00:11 +00:00
Adds support for the SCTP_PORT_REUSE option
Fixes a refcount bug found in the process Obtained from: With the help of Michael Tuexen
This commit is contained in:
parent
f6d4a8a77b
commit
6d9e8f2b3a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=181054
@ -110,6 +110,7 @@ struct sctp_paramhdr {
|
||||
#define SCTP_CONTEXT 0x0000001a /* rw */
|
||||
/* explict EOR signalling */
|
||||
#define SCTP_EXPLICIT_EOR 0x0000001b
|
||||
#define SCTP_REUSE_PORT 0x0000001c /* rw */
|
||||
|
||||
/*
|
||||
* read-only options
|
||||
@ -418,7 +419,6 @@ struct sctp_error_unrecognized_chunk {
|
||||
#define SCTP_PCB_FLAGS_BOUNDALL 0x00000004
|
||||
#define SCTP_PCB_FLAGS_ACCEPTING 0x00000008
|
||||
#define SCTP_PCB_FLAGS_UNBOUND 0x00000010
|
||||
#define SCTP_PCB_FLAGS_LISTENING 0x00000020
|
||||
#define SCTP_PCB_FLAGS_CLOSE_IP 0x00040000
|
||||
#define SCTP_PCB_FLAGS_WAS_CONNECTED 0x00080000
|
||||
#define SCTP_PCB_FLAGS_WAS_ABORTED 0x00100000
|
||||
@ -449,7 +449,6 @@ struct sctp_error_unrecognized_chunk {
|
||||
#define SCTP_PCB_FLAGS_DO_ASCONF 0x00000020
|
||||
#define SCTP_PCB_FLAGS_AUTO_ASCONF 0x00000040
|
||||
#define SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE 0x00000080
|
||||
|
||||
/* socket options */
|
||||
#define SCTP_PCB_FLAGS_NODELAY 0x00000100
|
||||
#define SCTP_PCB_FLAGS_AUTOCLOSE 0x00000200
|
||||
@ -467,7 +466,7 @@ struct sctp_error_unrecognized_chunk {
|
||||
#define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x00400000
|
||||
#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4 0x00800000
|
||||
#define SCTP_PCB_FLAGS_MULTIPLE_ASCONFS 0x01000000
|
||||
|
||||
#define SCTP_PCB_FLAGS_PORTREUSE 0x02000000
|
||||
/*-
|
||||
* mobility_features parameters (by micchie).Note
|
||||
* these features are applied against the
|
||||
|
@ -11718,6 +11718,12 @@ sctp_sosend(struct socket *so,
|
||||
struct sctp_inpcb *inp;
|
||||
int error, use_rcvinfo = 0;
|
||||
struct sctp_sndrcvinfo srcv;
|
||||
struct sockaddr *addr_to_use;
|
||||
|
||||
#ifdef INET6
|
||||
struct sockaddr_in sin;
|
||||
|
||||
#endif
|
||||
|
||||
inp = (struct sctp_inpcb *)so->so_pcb;
|
||||
if (control) {
|
||||
@ -11728,7 +11734,19 @@ sctp_sosend(struct socket *so,
|
||||
use_rcvinfo = 1;
|
||||
}
|
||||
}
|
||||
error = sctp_lower_sosend(so, addr, uio, top,
|
||||
addr_to_use = addr;
|
||||
#ifdef INET6
|
||||
if ((addr) && (addr->sa_family == AF_INET6)) {
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *)addr;
|
||||
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
|
||||
in6_sin6_2_sin(&sin, sin6);
|
||||
addr_to_use = (struct sockaddr *)&sin;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
error = sctp_lower_sosend(so, addr_to_use, uio, top,
|
||||
control,
|
||||
flags,
|
||||
use_rcvinfo, &srcv
|
||||
|
@ -860,8 +860,7 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
ephead = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(
|
||||
(lport + rport), SCTP_BASE_INFO(hashtcpmark))];
|
||||
ephead = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport), SCTP_BASE_INFO(hashtcpmark))];
|
||||
/*
|
||||
* Ok now for each of the guys in this bucket we must look and see:
|
||||
* - Does the remote port match. - Does there single association's
|
||||
@ -1432,6 +1431,7 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
|
||||
/* unsupported family */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (head == NULL)
|
||||
return (NULL);
|
||||
LIST_FOREACH(inp, head, sctp_hash) {
|
||||
@ -1468,7 +1468,6 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
|
||||
}
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
}
|
||||
|
||||
if ((nam->sa_family == AF_INET) &&
|
||||
(sin->sin_addr.s_addr == INADDR_ANY)) {
|
||||
/* Can't hunt for one that has no address specified */
|
||||
@ -1555,6 +1554,106 @@ sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
static struct sctp_inpcb *
|
||||
sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id)
|
||||
{
|
||||
struct sctppcbhead *head;
|
||||
struct sctp_inpcb *t_inp;
|
||||
int fnd;
|
||||
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
|
||||
SCTP_BASE_INFO(hashmark))];
|
||||
LIST_FOREACH(t_inp, head, sctp_hash) {
|
||||
if (t_inp->sctp_lport != lport) {
|
||||
continue;
|
||||
}
|
||||
/* is it in the VRF in question */
|
||||
fnd = 0;
|
||||
if (t_inp->def_vrf_id == vrf_id)
|
||||
fnd = 1;
|
||||
if (!fnd)
|
||||
continue;
|
||||
|
||||
/* This one is in use. */
|
||||
/* check the v6/v4 binding issue */
|
||||
if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
|
||||
SCTP_IPV6_V6ONLY(t_inp)) {
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
||||
/* collision in V6 space */
|
||||
return (t_inp);
|
||||
} else {
|
||||
/* inp is BOUND_V4 no conflict */
|
||||
continue;
|
||||
}
|
||||
} else if (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
||||
/* t_inp is bound v4 and v6, conflict always */
|
||||
return (t_inp);
|
||||
} else {
|
||||
/* t_inp is bound only V4 */
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
|
||||
SCTP_IPV6_V6ONLY(inp)) {
|
||||
/* no conflict */
|
||||
continue;
|
||||
}
|
||||
/* else fall through to conflict */
|
||||
}
|
||||
return (t_inp);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp)
|
||||
{
|
||||
/* For 1-2-1 with port reuse */
|
||||
struct sctppcbhead *head;
|
||||
struct sctp_inpcb *tinp;
|
||||
|
||||
if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) {
|
||||
/* only works with port reuse on */
|
||||
return (-1);
|
||||
}
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
|
||||
return (0);
|
||||
}
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport,
|
||||
SCTP_BASE_INFO(hashmark))];
|
||||
/* Kick out all non-listeners to the TCP hash */
|
||||
LIST_FOREACH(tinp, head, sctp_hash) {
|
||||
if (tinp->sctp_lport != inp->sctp_lport) {
|
||||
continue;
|
||||
}
|
||||
if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
|
||||
continue;
|
||||
}
|
||||
if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
|
||||
continue;
|
||||
}
|
||||
if (tinp->sctp_socket->so_qlimit) {
|
||||
continue;
|
||||
}
|
||||
SCTP_INP_WLOCK(tinp);
|
||||
LIST_REMOVE(tinp, sctp_hash);
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(tinp->sctp_lport, SCTP_BASE_INFO(hashtcpmark))];
|
||||
tinp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL;
|
||||
LIST_INSERT_HEAD(head, tinp, sctp_hash);
|
||||
SCTP_INP_WUNLOCK(tinp);
|
||||
}
|
||||
SCTP_INP_WLOCK(inp);
|
||||
/* Pull from where he was */
|
||||
LIST_REMOVE(inp, sctp_hash);
|
||||
inp->sctp_flags &= ~SCTP_PCB_FLAGS_IN_TCPPOOL;
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport, SCTP_BASE_INFO(hashmark))];
|
||||
LIST_INSERT_HEAD(head, inp, sctp_hash);
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
SCTP_INP_RLOCK(inp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
struct sctp_inpcb *
|
||||
sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock,
|
||||
uint32_t vrf_id)
|
||||
@ -1600,24 +1699,8 @@ sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock,
|
||||
* further code to look at all TCP models.
|
||||
*/
|
||||
if (inp == NULL && find_tcp_pool) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SCTP_BASE_INFO(hashtblsize); i++) {
|
||||
/*
|
||||
* This is real gross, but we do NOT have a remote
|
||||
* port at this point depending on who is calling.
|
||||
* We must therefore look for ANY one that matches
|
||||
* our local port :/
|
||||
*/
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[i];
|
||||
if (LIST_FIRST(head)) {
|
||||
inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
|
||||
if (inp) {
|
||||
/* Found one */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashtcpmark))];
|
||||
inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
|
||||
}
|
||||
if (inp) {
|
||||
SCTP_INP_INCR_REF(inp);
|
||||
@ -2383,7 +2466,7 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp,
|
||||
LIST_REMOVE(stcb, sctp_tcblist);
|
||||
|
||||
/* Now insert the new_inp into the TCP connected hash */
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport + rport),
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport),
|
||||
SCTP_BASE_INFO(hashtcpmark))];
|
||||
|
||||
LIST_INSERT_HEAD(head, new_inp, sctp_hash);
|
||||
@ -2456,53 +2539,6 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp,
|
||||
SCTP_INP_WUNLOCK(old_inp);
|
||||
}
|
||||
|
||||
static int
|
||||
sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id)
|
||||
{
|
||||
struct sctppcbhead *head;
|
||||
struct sctp_inpcb *t_inp;
|
||||
int fnd;
|
||||
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
|
||||
SCTP_BASE_INFO(hashmark))];
|
||||
LIST_FOREACH(t_inp, head, sctp_hash) {
|
||||
if (t_inp->sctp_lport != lport) {
|
||||
continue;
|
||||
}
|
||||
/* is it in the VRF in question */
|
||||
fnd = 0;
|
||||
if (t_inp->def_vrf_id == vrf_id)
|
||||
fnd = 1;
|
||||
if (!fnd)
|
||||
continue;
|
||||
|
||||
/* This one is in use. */
|
||||
/* check the v6/v4 binding issue */
|
||||
if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
|
||||
SCTP_IPV6_V6ONLY(t_inp)) {
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
||||
/* collision in V6 space */
|
||||
return (1);
|
||||
} else {
|
||||
/* inp is BOUND_V4 no conflict */
|
||||
continue;
|
||||
}
|
||||
} else if (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
||||
/* t_inp is bound v4 and v6, conflict always */
|
||||
return (1);
|
||||
} else {
|
||||
/* t_inp is bound only V4 */
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
|
||||
SCTP_IPV6_V6ONLY(inp)) {
|
||||
/* no conflict */
|
||||
continue;
|
||||
}
|
||||
/* else fall through to conflict */
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2515,6 +2551,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
|
||||
struct sctppcbhead *head;
|
||||
struct sctp_inpcb *inp, *inp_tmp;
|
||||
struct inpcb *ip_inp;
|
||||
int port_reuse_active = 0;
|
||||
int bindall;
|
||||
int prison = 0;
|
||||
uint16_t lport;
|
||||
@ -2664,8 +2701,17 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
|
||||
* so we must lower it.
|
||||
*/
|
||||
SCTP_INP_DECR_REF(inp_tmp);
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
/* unlock info */
|
||||
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
||||
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) {
|
||||
/*
|
||||
* Ok, must be one-2-one and
|
||||
* allowing port re-use
|
||||
*/
|
||||
port_reuse_active = 1;
|
||||
goto continue_anyway;
|
||||
}
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_INFO_WUNLOCK();
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
@ -2681,23 +2727,40 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
|
||||
* so we must lower it.
|
||||
*/
|
||||
SCTP_INP_DECR_REF(inp_tmp);
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
/* unlock info */
|
||||
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
||||
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) {
|
||||
/*
|
||||
* Ok, must be one-2-one and
|
||||
* allowing port re-use
|
||||
*/
|
||||
port_reuse_active = 1;
|
||||
goto continue_anyway;
|
||||
}
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_INFO_WUNLOCK();
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
}
|
||||
}
|
||||
continue_anyway:
|
||||
SCTP_INP_WLOCK(inp);
|
||||
if (bindall) {
|
||||
/* verify that no lport is not used by a singleton */
|
||||
if (sctp_isport_inuse(inp, lport, vrf_id)) {
|
||||
if ((port_reuse_active == 0) &&
|
||||
(inp_tmp = sctp_isport_inuse(inp, lport, vrf_id))
|
||||
) {
|
||||
/* Sorry someone already has this one bound */
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
SCTP_INP_INFO_WUNLOCK();
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
||||
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) {
|
||||
port_reuse_active = 1;
|
||||
} else {
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
SCTP_INP_INFO_WUNLOCK();
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -2736,7 +2799,7 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
|
||||
|
||||
done = 0;
|
||||
while (!done) {
|
||||
if (sctp_isport_inuse(inp, htons(candidate), inp->def_vrf_id) == 0) {
|
||||
if (sctp_isport_inuse(inp, htons(candidate), inp->def_vrf_id) == NULL) {
|
||||
done = 1;
|
||||
}
|
||||
if (!done) {
|
||||
@ -2884,12 +2947,19 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
|
||||
inp->laddr_count++;
|
||||
}
|
||||
/* find the bucket */
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
|
||||
SCTP_BASE_INFO(hashmark))];
|
||||
if (port_reuse_active) {
|
||||
/* Put it into tcp 1-2-1 hash */
|
||||
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport),
|
||||
SCTP_BASE_INFO(hashtcpmark))];
|
||||
inp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL;
|
||||
} else {
|
||||
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
|
||||
SCTP_BASE_INFO(hashmark))];
|
||||
}
|
||||
/* put it in the bucket */
|
||||
LIST_INSERT_HEAD(head, inp, sctp_hash);
|
||||
SCTPDBG(SCTP_DEBUG_PCB1, "Main hash to bind at head:%p, bound port:%d\n",
|
||||
head, ntohs(lport));
|
||||
SCTPDBG(SCTP_DEBUG_PCB1, "Main hash to bind at head:%p, bound port:%d - in tcp_pool=%d\n",
|
||||
head, ntohs(lport), port_reuse_active);
|
||||
/* set in the port */
|
||||
inp->sctp_lport = lport;
|
||||
|
||||
@ -3834,7 +3904,9 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
|
||||
return (NULL);
|
||||
}
|
||||
SCTP_INP_RLOCK(inp);
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) {
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) &&
|
||||
((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) ||
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED))) {
|
||||
/*
|
||||
* If its in the TCP pool, its NOT allowed to create an
|
||||
* association. The parent listener needs to call
|
||||
@ -5639,6 +5711,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* we must validate the state again
|
||||
* here
|
||||
*/
|
||||
add_it_now:
|
||||
if (stcb->asoc.state == 0) {
|
||||
/* the assoc was freed? */
|
||||
return (-7);
|
||||
@ -5661,9 +5734,18 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* strange, address is in another
|
||||
* assoc? straighten out locks.
|
||||
*/
|
||||
if (stcb_tmp)
|
||||
if (stcb_tmp) {
|
||||
if (SCTP_GET_STATE(&stcb_tmp->asoc) & SCTP_STATE_COOKIE_WAIT) {
|
||||
/*
|
||||
* in setup state we
|
||||
* abort this guy
|
||||
*/
|
||||
sctp_abort_an_association(stcb_tmp->sctp_ep,
|
||||
stcb_tmp, 1, NULL, 0);
|
||||
goto add_it_now;
|
||||
}
|
||||
SCTP_TCB_UNLOCK(stcb_tmp);
|
||||
|
||||
}
|
||||
if (stcb->asoc.state == 0) {
|
||||
/* the assoc was freed? */
|
||||
return (-12);
|
||||
@ -5708,6 +5790,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* we must validate the state again
|
||||
* here
|
||||
*/
|
||||
add_it_now6:
|
||||
if (stcb->asoc.state == 0) {
|
||||
/* the assoc was freed? */
|
||||
return (-16);
|
||||
@ -5739,7 +5822,16 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
|
||||
* assoc? straighten out locks.
|
||||
*/
|
||||
if (stcb_tmp)
|
||||
SCTP_TCB_UNLOCK(stcb_tmp);
|
||||
if (SCTP_GET_STATE(&stcb_tmp->asoc) & SCTP_STATE_COOKIE_WAIT) {
|
||||
/*
|
||||
* in setup state we
|
||||
* abort this guy
|
||||
*/
|
||||
sctp_abort_an_association(stcb_tmp->sctp_ep,
|
||||
stcb_tmp, 1, NULL, 0);
|
||||
goto add_it_now6;
|
||||
}
|
||||
SCTP_TCB_UNLOCK(stcb_tmp);
|
||||
|
||||
if (stcb->asoc.state == 0) {
|
||||
/* the assoc was freed? */
|
||||
|
@ -599,6 +599,8 @@ int sctp_is_vtag_good(struct sctp_inpcb *, uint32_t, struct timeval *, int);
|
||||
|
||||
int sctp_destination_is_reachable(struct sctp_tcb *, struct sockaddr *);
|
||||
|
||||
int sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp);
|
||||
|
||||
/*-
|
||||
* Null in last arg inpcb indicate run on ALL ep's. Specific inp in last arg
|
||||
* indicates run on ONLY assoc's of the specified endpoint.
|
||||
|
@ -1390,7 +1390,8 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
|
||||
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
}
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) {
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) &&
|
||||
(sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE))) {
|
||||
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
return (EINVAL);
|
||||
}
|
||||
@ -1461,6 +1462,7 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
|
||||
/* FIX ME: do we want to pass in a vrf on the connect call? */
|
||||
vrf_id = inp->def_vrf_id;
|
||||
|
||||
|
||||
/* We are GOOD to go */
|
||||
stcb = sctp_aloc_assoc(inp, sa, 1, &error, 0, vrf_id,
|
||||
(struct thread *)p
|
||||
@ -1639,6 +1641,20 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize,
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case SCTP_REUSE_PORT:
|
||||
{
|
||||
uint32_t *value;
|
||||
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
|
||||
/* Can't do this for a 1-m socket */
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
SCTP_CHECK_AND_CAST(value, optval, uint32_t, *optsize);
|
||||
*value = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE);
|
||||
*optsize = sizeof(uint32_t);
|
||||
}
|
||||
break;
|
||||
case SCTP_PARTIAL_DELIVERY_POINT:
|
||||
{
|
||||
uint32_t *value;
|
||||
@ -2498,8 +2514,10 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize,
|
||||
break;
|
||||
}
|
||||
/* copy in the list */
|
||||
for (i = 0; i < hmaclist->num_algo; i++)
|
||||
shmac->shmac_number_of_idents = hmaclist->num_algo;
|
||||
for (i = 0; i < hmaclist->num_algo; i++) {
|
||||
shmac->shmac_idents[i] = hmaclist->hmac[i];
|
||||
}
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
*optsize = size;
|
||||
break;
|
||||
@ -2696,6 +2714,25 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
|
||||
}
|
||||
SCTP_INP_WUNLOCK(inp);
|
||||
break;
|
||||
case SCTP_REUSE_PORT:
|
||||
{
|
||||
SCTP_CHECK_AND_CAST(mopt, optval, uint32_t, optsize);
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 0) {
|
||||
/* Can't set it after we are bound */
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
|
||||
/* Can't do this for a 1-m socket */
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (optval)
|
||||
sctp_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE);
|
||||
else
|
||||
sctp_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE);
|
||||
}
|
||||
break;
|
||||
case SCTP_PARTIAL_DELIVERY_POINT:
|
||||
{
|
||||
uint32_t *value;
|
||||
@ -3017,20 +3054,26 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
|
||||
{
|
||||
struct sctp_hmacalgo *shmac;
|
||||
sctp_hmaclist_t *hmaclist;
|
||||
uint32_t hmacid;
|
||||
size_t size, i, found;
|
||||
uint16_t hmacid;
|
||||
uint32_t i;
|
||||
|
||||
size_t found;
|
||||
|
||||
SCTP_CHECK_AND_CAST(shmac, optval, struct sctp_hmacalgo, optsize);
|
||||
size = (optsize - sizeof(*shmac)) / sizeof(shmac->shmac_idents[0]);
|
||||
hmaclist = sctp_alloc_hmaclist(size);
|
||||
if (optsize < sizeof(struct sctp_hmacalgo) + shmac->shmac_number_of_idents * sizeof(uint16_t)) {
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
hmaclist = sctp_alloc_hmaclist(shmac->shmac_number_of_idents);
|
||||
if (hmaclist == NULL) {
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOMEM);
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < size; i++) {
|
||||
for (i = 0; i < shmac->shmac_number_of_idents; i++) {
|
||||
hmacid = shmac->shmac_idents[i];
|
||||
if (sctp_auth_add_hmacid(hmaclist, (uint16_t) hmacid)) {
|
||||
if (sctp_auth_add_hmacid(hmaclist, hmacid)) {
|
||||
/* invalid HMACs were found */ ;
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
error = EINVAL;
|
||||
@ -4098,7 +4141,8 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
|
||||
}
|
||||
}
|
||||
/* Now do we connect? */
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) {
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) &&
|
||||
(sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE))) {
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
error = EINVAL;
|
||||
goto out_now;
|
||||
@ -4117,7 +4161,7 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
|
||||
} else {
|
||||
/*
|
||||
* We increment here since sctp_findassociation_ep_addr()
|
||||
* wil do a decrement if it finds the stcb as long as the
|
||||
* will do a decrement if it finds the stcb as long as the
|
||||
* locked tcb (last argument) is NOT a TCB.. aka NULL.
|
||||
*/
|
||||
SCTP_INP_INCR_REF(inp);
|
||||
@ -4183,6 +4227,64 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
|
||||
return (ECONNRESET);
|
||||
}
|
||||
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) {
|
||||
/* See if we have a listener */
|
||||
struct sctp_inpcb *tinp;
|
||||
union sctp_sockstore store, *sp;
|
||||
|
||||
sp = &store;
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
|
||||
/* not bound all */
|
||||
struct sctp_laddr *laddr;
|
||||
|
||||
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
|
||||
memcpy(&store, &laddr->ifa->address, sizeof(store));
|
||||
sp->sin.sin_port = inp->sctp_lport;
|
||||
tinp = sctp_pcb_findep(&sp->sa, 0, 0, inp->def_vrf_id);
|
||||
if (tinp && (tinp != inp) &&
|
||||
((tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) == 0) &&
|
||||
((tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) &&
|
||||
(tinp->sctp_socket->so_qlimit)) {
|
||||
/*
|
||||
* we have a listener already and
|
||||
* its not this inp.
|
||||
*/
|
||||
SCTP_INP_DECR_REF(tinp);
|
||||
return (EADDRINUSE);
|
||||
} else if (tinp) {
|
||||
SCTP_INP_DECR_REF(tinp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Setup a local addr bound all */
|
||||
memset(&store, 0, sizeof(store));
|
||||
store.sin.sin_port = inp->sctp_lport;
|
||||
#ifdef INET6
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
|
||||
store.sa.sa_family = AF_INET6;
|
||||
store.sa.sa_len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
#endif
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
|
||||
store.sa.sa_family = AF_INET;
|
||||
store.sa.sa_len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
tinp = sctp_pcb_findep(&sp->sa, 0, 0, inp->def_vrf_id);
|
||||
if (tinp && (tinp != inp) &&
|
||||
((tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) == 0) &&
|
||||
((tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) &&
|
||||
(tinp->sctp_socket->so_qlimit)) {
|
||||
/*
|
||||
* we have a listener already and its not
|
||||
* this inp.
|
||||
*/
|
||||
SCTP_INP_DECR_REF(tinp);
|
||||
return (EADDRINUSE);
|
||||
} else if (tinp) {
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
}
|
||||
}
|
||||
}
|
||||
SCTP_INP_RLOCK(inp);
|
||||
#ifdef SCTP_LOCK_LOGGING
|
||||
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) {
|
||||
@ -4196,30 +4298,36 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
return (error);
|
||||
}
|
||||
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
|
||||
/*
|
||||
* The unlucky case - We are in the tcp pool with this guy.
|
||||
* - Someone else is in the main inp slot. - We must move
|
||||
* this guy (the listener) to the main slot - We must then
|
||||
* move the guy that was listener to the TCP Pool.
|
||||
*/
|
||||
if (sctp_swap_inpcb_for_listen(inp)) {
|
||||
goto in_use;
|
||||
}
|
||||
}
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
|
||||
/* We are already connected AND the TCP model */
|
||||
in_use:
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
SOCK_UNLOCK(so);
|
||||
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE);
|
||||
return (EADDRINUSE);
|
||||
}
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
|
||||
/* We must do a bind. */
|
||||
SOCK_UNLOCK(so);
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
if ((error = sctp_inpcb_bind(so, NULL, NULL, p))) {
|
||||
/* bind error, probably perm */
|
||||
return (error);
|
||||
}
|
||||
SOCK_LOCK(so);
|
||||
} else {
|
||||
if (backlog != 0) {
|
||||
inp->sctp_flags |= SCTP_PCB_FLAGS_LISTENING;
|
||||
} else {
|
||||
inp->sctp_flags &= ~SCTP_PCB_FLAGS_LISTENING;
|
||||
}
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
}
|
||||
/* It appears for 7.0 and on, we must always call this. */
|
||||
solisten_proto(so, backlog);
|
||||
|
@ -45,9 +45,10 @@ extern struct pr_usrreqs sctp_usrreqs;
|
||||
|
||||
#define sctp_feature_on(inp, feature) (inp->sctp_features |= feature)
|
||||
#define sctp_feature_off(inp, feature) (inp->sctp_features &= ~feature)
|
||||
#define sctp_is_feature_on(inp, feature) (inp->sctp_features & feature)
|
||||
#define sctp_is_feature_on(inp, feature) ((inp->sctp_features & feature) == feature)
|
||||
#define sctp_is_feature_off(inp, feature) ((inp->sctp_features & feature) == 0)
|
||||
|
||||
|
||||
/* managing mobility_feature in inpcb (by micchie) */
|
||||
#define sctp_mobility_feature_on(inp, feature) (inp->sctp_mobility_features |= feature)
|
||||
#define sctp_mobility_feature_off(inp, feature) (inp->sctp_mobility_features &= ~feature)
|
||||
|
@ -6351,7 +6351,7 @@ sctp_bindx_add_address(struct socket *so, struct sctp_inpcb *inp,
|
||||
* ep already and are binding. No remove going on
|
||||
* here.
|
||||
*/
|
||||
SCTP_INP_DECR_REF(inp);
|
||||
SCTP_INP_DECR_REF(lep);
|
||||
}
|
||||
if (lep == inp) {
|
||||
/* already bound to it.. ok */
|
||||
|
Loading…
Reference in New Issue
Block a user