diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index cc4c6283092c..6dfe5d42d8ae 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -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 diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 19bbbacb383f..3c61c698f4f3 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -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 diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 17c1da05c11a..55be58b62aed 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -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? */ diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 7f63dcd92ddf..0600e9381b09 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -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. diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 6c34d47e1ccc..2f2418147627 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -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); diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h index 3ef801c721fc..77f2cebbb9eb 100644 --- a/sys/netinet/sctp_var.h +++ b/sys/netinet/sctp_var.h @@ -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) diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 9a15d6bd0a6c..dc215bd2bd86 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -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 */