diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index a8bcec69fbbc..f51bf639c47d 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -91,7 +91,7 @@ struct sctp_paramhdr { /* Without this applied we will give V4 and V6 addresses on a V6 socket */ #define SCTP_I_WANT_MAPPED_V4_ADDR 0x0000000d #define SCTP_MAXSEG 0x0000000e -#define SCTP_DELAYED_ACK_TIME 0x0000000f +#define SCTP_DELAYED_SACK 0x0000000f #define SCTP_FRAGMENT_INTERLEAVE 0x00000010 #define SCTP_PARTIAL_DELIVERY_POINT 0x00000011 /* authentication support */ @@ -103,6 +103,7 @@ struct sctp_paramhdr { #define SCTP_USE_EXT_RCVINFO 0x00000017 #define SCTP_AUTO_ASCONF 0x00000018 /* rw */ #define SCTP_MAXBURST 0x00000019 /* rw */ +#define SCTP_MAX_BURST 0x00000019 /* rw */ /* assoc level context */ #define SCTP_CONTEXT 0x0000001a /* rw */ /* explict EOR signalling */ @@ -447,6 +448,9 @@ struct sctp_error_unrecognized_chunk { #define SCTP_PCB_FLAGS_NO_FRAGMENT 0x00100000 #define SCTP_PCB_FLAGS_EXPLICIT_EOR 0x00400000 +#define SCTP_SMALLEST_PMTU 512 /* smallest pmtu allowed when disabling PMTU + * discovery */ + #include #endif /* !_NETINET_SCTP_H_ */ diff --git a/sys/netinet/sctp_auth.c b/sys/netinet/sctp_auth.c index 42ba003b6201..2349c62f64e4 100644 --- a/sys/netinet/sctp_auth.c +++ b/sys/netinet/sctp_auth.c @@ -629,6 +629,8 @@ sctp_free_hmaclist(sctp_hmaclist_t * list) int sctp_auth_add_hmacid(sctp_hmaclist_t * list, uint16_t hmac_id) { + int i; + if (list == NULL) return (-1); if (list->num_algo == list->max_algo) { @@ -648,6 +650,13 @@ sctp_auth_add_hmacid(sctp_hmaclist_t * list, uint16_t hmac_id) (hmac_id != SCTP_AUTH_HMAC_ID_MD5)) { return (-1); } + /* Now is it already in the list */ + for (i = 0; i < list->num_algo; i++) { + if (list->hmac[i] == hmac_id) { + /* already in list */ + return (-1); + } + } SCTPDBG(SCTP_DEBUG_AUTH1, "SCTP: add HMAC id %u to list\n", hmac_id); list->hmac[list->num_algo++] = hmac_id; return (0); @@ -1338,7 +1347,11 @@ sctp_auth_setactivekey(struct sctp_tcb *stcb, uint16_t keyid) skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid); if (skey == NULL) { /* if not on the assoc, find the key on the endpoint */ + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); SCTP_INP_RLOCK(stcb->sctp_ep); + SCTP_TCB_LOCK(stcb); + atomic_add_int(&stcb->asoc.refcnt, -1); skey = sctp_find_sharedkey(&stcb->sctp_ep->sctp_ep.shared_keys, keyid); using_ep_key = 1; diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index b695d81e802b..c01b3c343c5a 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -60,6 +60,8 @@ __FBSDID("$FreeBSD$"); /* Number of addresses where we just skip the counting */ #define SCTP_COUNT_LIMIT 40 +#define SCTP_ZERO_COPY_TICK_DELAY (((100 * hz) + 999) / 1000) + /* Number of ticks to delay before running * iterator on an address change. */ @@ -542,8 +544,9 @@ __FBSDID("$FreeBSD$"); #define SCTP_TIMER_TYPE_EARLYFR 17 #define SCTP_TIMER_TYPE_ASOCKILL 18 #define SCTP_TIMER_TYPE_ADDR_WQ 19 +#define SCTP_TIMER_TYPE_ZERO_COPY 20 /* add new timers here - and increment LAST */ -#define SCTP_TIMER_TYPE_LAST 20 +#define SCTP_TIMER_TYPE_LAST 21 #define SCTP_IS_TIMER_TYPE_VALID(t) (((t) > SCTP_TIMER_TYPE_NONE) && \ ((t) < SCTP_TIMER_TYPE_LAST)) @@ -654,11 +657,12 @@ __FBSDID("$FreeBSD$"); #define SCTP_DEF_MAX_INIT 8 #define SCTP_DEF_MAX_SEND 10 -#define SCTP_DEF_MAX_PATH_RTX 4 +#define SCTP_DEF_MAX_PATH_RTX 5 #define SCTP_DEF_PMTU_RAISE_SEC 600 /* 10 min between raise attempts */ #define SCTP_DEF_PMTU_MIN 600 + #define SCTP_MSEC_IN_A_SEC 1000 #define SCTP_USEC_IN_A_SEC 1000000 #define SCTP_NSEC_IN_A_SEC 1000000000 @@ -810,6 +814,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_FROM_SCTP_ASCONF 0x80000000 #define SCTP_FROM_SCTP_OUTPUT 0x90000000 #define SCTP_FROM_SCTP_PEELOFF 0xa0000000 +#define SCTP_FROM_SCTP_PANDA 0xb0000000 /* Location ID's */ #define SCTP_LOC_1 0x00000001 diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index a021fed9f2a7..e1277fec45a2 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -1609,7 +1609,6 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, * only validate the FIRST fragment so the bit must be set. */ strmseq = ntohs(ch->dp.stream_sequence); - #ifdef SCTP_ASOCLOG_OF_TSNS asoc->in_tsnlog[asoc->tsn_in_at].tsn = tsn; asoc->in_tsnlog[asoc->tsn_in_at].strm = strmno; @@ -1623,6 +1622,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, } #endif if ((chunk_flags & SCTP_DATA_FIRST_FRAG) && + (TAILQ_EMPTY(&asoc->resetHead)) && (chunk_flags & SCTP_DATA_UNORDERED) == 0 && (compare_with_wrap(asoc->strmin[strmno].last_sequence_delivered, strmseq, MAX_SEQ) || @@ -2006,11 +2006,8 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, * and proceessed by TSN order. It is only the * singletons I must worry about. */ - struct sctp_stream_reset_list *liste; - if (((liste = TAILQ_FIRST(&asoc->resetHead)) != NULL) && - ((compare_with_wrap(tsn, ntohl(liste->tsn), MAX_TSN)) || - (tsn == ntohl(liste->tsn))) + ((compare_with_wrap(tsn, liste->tsn, MAX_TSN))) ) { /* * yep its past where we need to reset... go @@ -2095,8 +2092,8 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, SCTP_SET_TSN_PRESENT(asoc->mapping_array, gap); /* check the special flag for stream resets */ if (((liste = TAILQ_FIRST(&asoc->resetHead)) != NULL) && - ((compare_with_wrap(asoc->cumulative_tsn, ntohl(liste->tsn), MAX_TSN)) || - (asoc->cumulative_tsn == ntohl(liste->tsn))) + ((compare_with_wrap(asoc->cumulative_tsn, liste->tsn, MAX_TSN)) || + (asoc->cumulative_tsn == liste->tsn)) ) { /* * we have finished working through the backlogged TSN's now @@ -2124,7 +2121,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, } } else if (ctl) { /* more than one in queue */ - while (!compare_with_wrap(ctl->sinfo_tsn, ntohl(liste->tsn), MAX_TSN)) { + while (!compare_with_wrap(ctl->sinfo_tsn, liste->tsn, MAX_TSN)) { /* * if ctl->sinfo_tsn is <= liste->tsn we can * process it which is the NOT of @@ -2668,12 +2665,12 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length, /* unknown chunk type, use bit rules */ if (ch->ch.chunk_type & 0x40) { /* Add a error report to the queue */ - struct mbuf *mm; + struct mbuf *merr; struct sctp_paramhdr *phd; - mm = sctp_get_mbuf_for_msg(sizeof(*phd), 0, M_DONTWAIT, 1, MT_DATA); - if (mm) { - phd = mtod(mm, struct sctp_paramhdr *); + merr = sctp_get_mbuf_for_msg(sizeof(*phd), 0, M_DONTWAIT, 1, MT_DATA); + if (merr) { + phd = mtod(merr, struct sctp_paramhdr *); /* * We cheat and use param * type since we did not @@ -2686,14 +2683,14 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length, htons(SCTP_CAUSE_UNRECOG_CHUNK); phd->param_length = htons(chk_length + sizeof(*phd)); - SCTP_BUF_LEN(mm) = sizeof(*phd); - SCTP_BUF_NEXT(mm) = SCTP_M_COPYM(m, *offset, + SCTP_BUF_LEN(merr) = sizeof(*phd); + SCTP_BUF_NEXT(merr) = SCTP_M_COPYM(m, *offset, SCTP_SIZE32(chk_length), M_DONTWAIT); - if (SCTP_BUF_NEXT(mm)) { - sctp_queue_op_err(stcb, mm); + if (SCTP_BUF_NEXT(merr)) { + sctp_queue_op_err(stcb, merr); } else { - sctp_m_freem(mm); + sctp_m_freem(merr); } } } @@ -5893,7 +5890,7 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, fwd_sz -= sizeof(*fwd); { /* New method. */ - int num_str, i; + unsigned int num_str; num_str = fwd_sz / sizeof(struct sctp_strseq); for (i = 0; i < num_str; i++) { diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index bacdfe3440b5..e80c076b45a0 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -322,23 +322,8 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, &abort_flag, (struct sctp_chunkhdr *)cp); if (abort_flag) { /* Send an abort and notify peer */ - if (op_err != NULL) { - sctp_send_operr_to(m, iphlen, op_err, - cp->init.initiate_tag, vrf_id, - table_id); - } else { - /* - * Just notify (abort_assoc does this if we send an - * abort). - */ - sctp_abort_notification(stcb, 0); - /* - * No sense in further INIT's since we will get the - * same param back - */ - sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); - *abort_no_unlock = 1; - } + sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_CAUSE_PROTOCOL_VIOLATION, op_err); + *abort_no_unlock = 1; return (-1); } asoc = &stcb->asoc; @@ -393,8 +378,6 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, */ if (retval == -3) { /* We abort with an error of missing mandatory param */ - struct mbuf *op_err; - op_err = sctp_generate_invmanparam(SCTP_CAUSE_MISSING_PARAM); if (op_err) { @@ -570,6 +553,7 @@ sctp_handle_shutdown(struct sctp_shutdown_chunk *cp, /* goto SHUTDOWN_RECEIVED state to block new requests */ if (stcb->sctp_socket) { if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) && + (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT)) { asoc->state = SCTP_STATE_SHUTDOWN_RECEIVED; /* @@ -2160,7 +2144,14 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, * another and get the tcb in the right place. */ sctp_move_pcb_and_assoc(*inp_p, inp, *stcb); + + atomic_add_int(&(*stcb)->asoc.refcnt, 1); + SCTP_TCB_UNLOCK((*stcb)); + sctp_pull_off_control_to_new_inp((*inp_p), inp, *stcb, M_NOWAIT); + SCTP_TCB_LOCK((*stcb)); + atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); + /* * now we must check to see if we were aborted while @@ -3472,6 +3463,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, /* validate chunk header length... */ if (ntohs(ch->chunk_length) < sizeof(*ch)) { + SCTPDBG(SCTP_DEBUG_INPUT1, "Invalid header length %d\n", + ntohs(ch->chunk_length)); return (NULL); } /* @@ -3483,6 +3476,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, SCTP_TCB_LOCK_ASSERT(locked_tcb); } if (ch->chunk_type == SCTP_INITIATION) { + SCTPDBG(SCTP_DEBUG_INPUT1, "Its an INIT of len:%d vtag:%x\n", + ntohs(ch->chunk_length), vtag_in); if (vtag_in != 0) { /* protocol error- silently discard... */ SCTP_STAT_INCR(sctps_badvtag); @@ -4707,6 +4702,7 @@ sctp_input(i_pak, off) mlen = SCTP_HEADER_LEN(i_pak); iphlen = off; m = SCTP_HEADER_TO_CHAIN(i_pak); + net = NULL; SCTP_STAT_INCR(sctps_recvpackets); SCTP_STAT_INCR_COUNTER64(sctps_inpackets); diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 39de2af6f662..0b1bac12ac2b 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -4047,13 +4047,14 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, limit = ntohs(cp->chunk_length) - sizeof(struct sctp_init_chunk); at = param_offset; op_err = NULL; - + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Check for unrecognized param's\n"); phdr = sctp_get_next_param(mat, at, ¶ms, sizeof(params)); while ((phdr != NULL) && ((size_t)limit >= sizeof(struct sctp_paramhdr))) { ptype = ntohs(phdr->param_type); plen = ntohs(phdr->param_length); if ((plen > limit) || (plen < sizeof(struct sctp_paramhdr))) { /* wacked parameter */ + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error %d\n", plen); goto invalid_size; } limit -= SCTP_SIZE32(plen); @@ -4078,18 +4079,21 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, case SCTP_CHUNK_LIST: case SCTP_SUPPORTED_CHUNK_EXT: if (padded_size > (sizeof(struct sctp_supported_chunk_types_param) + (sizeof(uint8_t) * SCTP_MAX_SUPPORTED_EXT))) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error chklist %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_SUPPORTED_ADDRTYPE: if (padded_size > SCTP_MAX_ADDR_PARAMS_SIZE) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error supaddrtype %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_RANDOM: if (padded_size > (sizeof(struct sctp_auth_random) + SCTP_RANDOM_MAX_SIZE)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error random %d\n", plen); goto invalid_size; } at += padded_size; @@ -4099,6 +4103,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, case SCTP_ADD_IP_ADDRESS: if ((padded_size != sizeof(struct sctp_asconf_addrv4_param)) && (padded_size != sizeof(struct sctp_asconf_addr_param))) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error setprim %d\n", plen); goto invalid_size; } at += padded_size; @@ -4106,18 +4111,21 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, /* Param's with a fixed size */ case SCTP_IPV4_ADDRESS: if (padded_size != sizeof(struct sctp_ipv4addr_param)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ipv4 addr %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_IPV6_ADDRESS: if (padded_size != sizeof(struct sctp_ipv6addr_param)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ipv6 addr %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_COOKIE_PRESERVE: if (padded_size != sizeof(struct sctp_cookie_perserve_param)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error cookie-preserve %d\n", plen); goto invalid_size; } at += padded_size; @@ -4125,24 +4133,28 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, case SCTP_ECN_NONCE_SUPPORTED: case SCTP_PRSCTP_SUPPORTED: if (padded_size != sizeof(struct sctp_paramhdr)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ecnnonce/prsctp %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_ECN_CAPABLE: if (padded_size != sizeof(struct sctp_ecn_supported_param)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ecn %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_ULP_ADAPTATION: if (padded_size != sizeof(struct sctp_adaptation_layer_indication)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error adapatation %d\n", plen); goto invalid_size; } at += padded_size; break; case SCTP_SUCCESS_REPORT: if (padded_size != sizeof(struct sctp_asconf_paramhdr)) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error success %d\n", plen); goto invalid_size; } at += padded_size; @@ -4152,7 +4164,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, /* We can NOT handle HOST NAME addresses!! */ int l_len; - SCTPDBG(SCTP_DEBUG_OUTPUT4, "Can't handle hostname addresses.. abort processing\n"); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Can't handle hostname addresses.. abort processing\n"); *abort_processing = 1; if (op_err == NULL) { /* Ok need to try to get a mbuf */ @@ -4210,8 +4222,10 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, * we do not recognize the parameter figure out what * we do. */ + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Hit default param %x\n", ptype); if ((ptype & 0x4000) == 0x4000) { /* Report bit is set?? */ + SCTPDBG(SCTP_DEBUG_OUTPUT1, "report op err\n"); if (op_err == NULL) { int l_len; @@ -4264,9 +4278,11 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, } more_processing: if ((ptype & 0x8000) == 0x0000) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "stop proc\n"); return (op_err); } else { /* skip this chunk and continue processing */ + SCTPDBG(SCTP_DEBUG_OUTPUT1, "move on\n"); at += SCTP_SIZE32(plen); } break; @@ -4276,6 +4292,7 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, } return (op_err); invalid_size: + SCTPDBG(SCTP_DEBUG_OUTPUT1, "abort flag set\n"); *abort_processing = 1; if ((op_err == NULL) && phdr) { int l_len; @@ -5229,10 +5246,10 @@ sctp_get_frag_point(struct sctp_tcb *stcb, ovh = SCTP_MED_V4_OVERHEAD; } - if (stcb->sctp_ep->sctp_frag_point > asoc->smallest_mtu) + if (stcb->asoc.sctp_frag_point > asoc->smallest_mtu) siz = asoc->smallest_mtu - ovh; else - siz = (stcb->sctp_ep->sctp_frag_point - ovh); + siz = (stcb->asoc.sctp_frag_point - ovh); /* * if (siz > (MCLBYTES-sizeof(struct sctp_data_chunk))) { */ @@ -5824,7 +5841,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m, memset(ca, 0, sizeof(struct sctp_copy_all)); ca->inp = inp; - ca->sndrcv = *srcv; + memcpy(&ca->sndrcv, srcv, sizeof(struct sctp_nonpad_sndrcvinfo)); /* * take off the sendall flag, it would be bad if we failed to do * this :-0 @@ -7250,7 +7267,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, r_mtu = 0; to_out += chk->send_size; - if (to_out > mx_mtu) { + if ((to_out > mx_mtu) && no_fragmentflg) { #ifdef INVARIANTS panic("Exceeding mtu of %d out size is %d", mx_mtu, to_out); #else @@ -10588,7 +10605,21 @@ sctp_lower_sosend(struct socket *so, sndlen = SCTP_HEADER_LEN(i_pak); top = SCTP_HEADER_TO_CHAIN(i_pak); } - + /* + * Pre-screen address, if one is given the sin-len must be set + * correctly! + */ + if (addr) { + if ((addr->sa_family == AF_INET) && + (addr->sa_len != sizeof(struct sockaddr_in))) { + error = EINVAL; + goto out_unlocked; + } else if ((addr->sa_family == AF_INET6) && + (addr->sa_len != sizeof(struct sockaddr_in6))) { + error = EINVAL; + goto out_unlocked; + } + } hold_tcblock = 0; if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && @@ -10598,7 +10629,8 @@ sctp_lower_sosend(struct socket *so, goto out_unlocked; } if ((use_rcvinfo) && srcv) { - if (INVALID_SINFO_FLAG(srcv->sinfo_flags) || PR_SCTP_INVALID_POLICY(srcv->sinfo_flags)) { + if (INVALID_SINFO_FLAG(srcv->sinfo_flags) || + PR_SCTP_INVALID_POLICY(srcv->sinfo_flags)) { error = EINVAL; goto out_unlocked; } @@ -10769,7 +10801,6 @@ sctp_lower_sosend(struct socket *so, goto out_unlocked; } /* get an asoc/stcb struct */ - vrf_id = inp->def_vrf_id; stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0, vrf_id); if (stcb == NULL) { @@ -10936,7 +10967,7 @@ sctp_lower_sosend(struct socket *so, } if ((use_rcvinfo == 0) || (srcv == NULL)) { /* Grab the default stuff from the asoc */ - srcv = &stcb->asoc.def_send; + srcv = (struct sctp_sndrcvinfo *)&stcb->asoc.def_send; } /* we are now done with all control */ if (control) { diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 536ab721ce39..2b498b775bd1 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -452,6 +452,7 @@ sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, } SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); + (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_ADD_IP_ADDRESS; SCTP_IPI_ITERATOR_WQ_LOCK(); @@ -529,6 +530,7 @@ sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr, } SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); + (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = sctp_ifap; wi->action = SCTP_DEL_IP_ADDRESS; SCTP_IPI_ITERATOR_WQ_LOCK(); @@ -994,25 +996,32 @@ sctp_findassociation_ep_asocid(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int SCTP_INP_RLOCK(stcb->sctp_ep); if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { SCTP_INP_RUNLOCK(stcb->sctp_ep); - SCTP_INP_INFO_RUNLOCK(); - return (NULL); + continue; + } + if (want_lock) { + SCTP_TCB_LOCK(stcb); } - SCTP_TCB_LOCK(stcb); - SCTP_INP_RUNLOCK(stcb->sctp_ep); if (stcb->asoc.assoc_id == id) { /* candidate */ + SCTP_INP_RUNLOCK(stcb->sctp_ep); if (inp != stcb->sctp_ep) { /* * some other guy has the same id active (id * collision ??). */ - SCTP_TCB_UNLOCK(stcb); + if (want_lock) { + SCTP_TCB_UNLOCK(stcb); + } continue; } SCTP_INP_INFO_RUNLOCK(); return (stcb); + } else { + SCTP_INP_RUNLOCK(stcb->sctp_ep); + } + if (want_lock) { + SCTP_TCB_UNLOCK(stcb); } - SCTP_TCB_UNLOCK(stcb); } SCTP_INP_INFO_RUNLOCK(); return (NULL); @@ -1773,6 +1782,8 @@ sctp_inpcb_alloc(struct socket *so) SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp); return (EOPNOTSUPP); } + sctp_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); + inp->sctp_tcbhash = SCTP_HASH_INIT(sctp_pcbtblsize, &inp->sctp_hashmark); if (inp->sctp_tcbhash == NULL) { @@ -1957,6 +1968,7 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, } SCTP_INCR_LADDR_COUNT(); bzero(laddr, sizeof(*laddr)); + (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = oladdr->ifa; atomic_add_int(&laddr->ifa->refcount, 1); LIST_INSERT_HEAD(&new_inp->sctp_addr_list, laddr, @@ -2184,88 +2196,60 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p) } } } else { - /* - * get any port but lets make sure no one has any address - * with this port bound - */ + uint16_t first, last, candiate; + uint16_t count; + int done; - /* - * setup the inp to the top (I could use the union but this - * is just as easy - */ - uint32_t port_guess; - uint16_t port_attempt; - int not_done = 1; - int not_found = 1; + if (ip_inp->inp_flags & INP_HIGHPORT) { + first = ipport_hifirstauto; + last = ipport_hilastauto; + } else if (ip_inp->inp_flags & INP_LOWPORT) { + if (p && (error = + priv_check_cred(p->td_ucred, + PRIV_NETINET_RESERVEDPORT, + SUSER_ALLOWJAIL + ) + )) { + SCTP_INP_DECR_REF(inp); + SCTP_INP_WUNLOCK(inp); + SCTP_INP_INFO_WUNLOCK(); + return (error); + } + first = ipport_lowfirstauto; + last = ipport_lowlastauto; + } else { + first = ipport_firstauto; + last = ipport_lastauto; + } + if (first > last) { + uint16_t temp; - while (not_done) { - port_guess = sctp_select_initial_TSN(&inp->sctp_ep); - port_attempt = (port_guess & 0x0000ffff); - if (port_attempt == 0) { - goto next_half; - } - if (port_attempt < IPPORT_RESERVED) { - port_attempt += IPPORT_RESERVED; - } - not_found = 1; - vrf_id = inp->def_vrf_id; - if (sctp_isport_inuse(inp, htons(port_attempt), - vrf_id) == 1) { - /* got a port we can use */ - not_found = 0; - } - if (not_found == 1) { - /* We can use this port */ - not_done = 0; - continue; - } - /* try upper half */ - next_half: - port_attempt = ((port_guess >> 16) & 0x0000ffff); + temp = first; + first = last; + last = temp; + } + count = last - first + 1; /* number of candidates */ + candiate = first + sctp_select_initial_TSN(&inp->sctp_ep) % (count); - if (port_attempt == 0) { - goto last_try; + done = 0; + while (!done) { + if (sctp_isport_inuse(inp, htons(candiate), inp->def_vrf_id) == 0) { + done = 1; } - if (port_attempt < IPPORT_RESERVED) { - port_attempt += IPPORT_RESERVED; - } - not_found = 1; - vrf_id = inp->def_vrf_id; - if (sctp_isport_inuse(inp, htons(port_attempt), - vrf_id) == 1) { - /* got a port we can use */ - not_found = 0; - } - if (not_found == 1) { - /* We can use this port */ - not_done = 0; - continue; - } - /* try two half's added together */ - last_try: - port_attempt = (((port_guess >> 16) & 0x0000ffff) + - (port_guess & 0x0000ffff)); - if (port_attempt == 0) { - /* get a new random number */ - continue; - } - if (port_attempt < IPPORT_RESERVED) { - port_attempt += IPPORT_RESERVED; - } - not_found = 1; - vrf_id = inp->def_vrf_id; - if (sctp_isport_inuse(inp, htons(port_attempt), vrf_id) == 1) { - /* got a port we can use */ - not_found = 0; - } - if (not_found == 1) { - /* We can use this port */ - not_done = 0; - continue; + if (!done) { + if (--count == 0) { + SCTP_INP_DECR_REF(inp); + SCTP_INP_WUNLOCK(inp); + SCTP_INP_INFO_WUNLOCK(); + return (EADDRNOTAVAIL); + } + if (candiate == last) + candiate = first; + else + candiate = candiate + 1; } } - /* we don't get out of the loop until we have a port */ - lport = htons(port_attempt); + lport = htons(candiate); } SCTP_INP_DECR_REF(inp); if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE | @@ -4448,6 +4432,7 @@ sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act) } SCTP_INCR_LADDR_COUNT(); bzero(laddr, sizeof(*laddr)); + (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time); laddr->ifa = ifa; laddr->action = act; atomic_add_int(&ifa->refcount, 1); diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 230cbf50a8c0..ddcbf4d59414 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -118,6 +118,7 @@ struct sctp_laddr { uint32_t action; /* Used during asconf and adding if no-zero * src-addr selection will not consider this * address. */ + struct timeval start_time; /* time when this address was created */ }; struct sctp_block_entry { @@ -276,6 +277,9 @@ struct sctp_pcb { * change the secret key. The default is once a hour */ struct sctp_timer signature_change; + + /* Zero copy full buffer timer */ + struct sctp_timer zero_copy_timer; int def_cookie_life; /* defaults to 0 */ int auto_close_time; @@ -343,7 +347,7 @@ struct sctp_inpcb { uint32_t sctp_frag_point; uint32_t partial_delivery_point; uint32_t sctp_context; - struct sctp_sndrcvinfo def_send; + struct sctp_nonpad_sndrcvinfo def_send; /*- * These three are here for the sosend_dgram * (pkt, pkt_last and control). diff --git a/sys/netinet/sctp_peeloff.c b/sys/netinet/sctp_peeloff.c index df4afdc6ffaf..f35c7e648b3b 100644 --- a/sys/netinet/sctp_peeloff.c +++ b/sys/netinet/sctp_peeloff.c @@ -98,10 +98,12 @@ sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id) * stcb in the right place. */ sctp_move_pcb_and_assoc(inp, n_inp, stcb); + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK); + atomic_subtract_int(&stcb->asoc.refcnt, 1); - SCTP_TCB_UNLOCK(stcb); return (0); } @@ -131,6 +133,7 @@ sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error) *error = ENOMEM; SCTP_TCB_UNLOCK(stcb); return (NULL); + } n_inp = (struct sctp_inpcb *)newso->so_pcb; SOCK_LOCK(head); @@ -183,12 +186,14 @@ sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error) SCTP_INP_WUNLOCK(n_inp); SCTP_INP_WUNLOCK(inp); sctp_move_pcb_and_assoc(inp, n_inp, stcb); + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); /* * And now the final hack. We move data in the pending side i.e. * head to the new socket buffer. Let the GRUBBING begin :-0 */ sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK); + atomic_subtract_int(&stcb->asoc.refcnt, 1); - SCTP_TCB_UNLOCK(stcb); return (newso); } diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index 5611808703be..e50ccc5fa6cb 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -58,17 +58,6 @@ struct sctp_timer { uint32_t stopped_from; }; -struct sctp_nonpad_sndrcvinfo { - uint16_t sinfo_stream; - uint16_t sinfo_ssn; - uint16_t sinfo_flags; - uint32_t sinfo_ppid; - uint32_t sinfo_context; - uint32_t sinfo_timetolive; - uint32_t sinfo_tsn; - uint32_t sinfo_cumtsn; - sctp_assoc_t sinfo_assoc_id; -}; struct sctp_foo_stuff { struct sctp_inpcb *inp; @@ -472,6 +461,26 @@ struct sctp_tsn_log { uint16_t flgs; }; + + +/* This struct is here to cut out the compatiabilty + * pad that bulks up both the inp and stcb. The non + * pad portion MUST stay in complete sync with + * sctp_sndrcvinfo... i.e. if sinfo_xxxx is added + * this must be done here too. + */ +struct sctp_nonpad_sndrcvinfo { + uint16_t sinfo_stream; + uint16_t sinfo_ssn; + uint16_t sinfo_flags; + uint32_t sinfo_ppid; + uint32_t sinfo_context; + uint32_t sinfo_timetolive; + uint32_t sinfo_tsn; + uint32_t sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + /* * Here we have information about each individual association that we track. * We probably in production would be more dynamic. But for ease of @@ -487,7 +496,7 @@ struct sctp_association { struct timeval time_last_rcvd; struct timeval time_last_sent; struct timeval time_last_sat_advance; - struct sctp_sndrcvinfo def_send; /* default send parameters */ + struct sctp_nonpad_sndrcvinfo def_send; /* timers and such */ struct sctp_timer hb_timer; /* hb timer */ @@ -695,6 +704,7 @@ struct sctp_association { uint32_t my_rwnd; uint32_t my_last_reported_rwnd; uint32_t my_rwnd_control_len; + uint32_t sctp_frag_point; uint32_t total_output_queue_size; diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c index ce1ab45715af..fda947753699 100644 --- a/sys/netinet/sctp_sysctl.c +++ b/sys/netinet/sctp_sysctl.c @@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include - +#include /* * sysctl tunable variables */ @@ -112,6 +112,174 @@ uint32_t sctp_debug_on = 0; #endif + +/* It returns an upper limit. No filtering is done here */ +static unsigned int +number_of_addresses(struct sctp_inpcb *inp) +{ + int cnt; + struct sctp_vrf *vrf; + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa; + struct sctp_laddr *laddr; + + cnt = 0; + /* neither Mac OS X nor FreeBSD support mulitple routing functions */ + if ((vrf = sctp_find_vrf(inp->def_vrf_id)) == NULL) { + return (0); + } + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if ((sctp_ifa->address.sa.sa_family == AF_INET) || + (sctp_ifa->address.sa.sa_family == AF_INET6)) { + cnt++; + } + } + } + } else { + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if ((laddr->ifa->address.sa.sa_family == AF_INET) || + (laddr->ifa->address.sa.sa_family == AF_INET6)) { + cnt++; + } + } + } + return (cnt); +} + +static int +copy_out_local_addresses(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sysctl_req *req) +{ + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa; + int loopback_scope, ipv4_local_scope, local_scope, site_scope; + int ipv4_addr_legal, ipv6_addr_legal; + struct sctp_vrf *vrf; + struct xsctp_laddr xladdr; + struct sctp_laddr *laddr; + int error; + + /* Turn on all the appropriate scope */ + if (stcb) { + /* use association specific values */ + loopback_scope = stcb->asoc.loopback_scope; + ipv4_local_scope = stcb->asoc.ipv4_local_scope; + local_scope = stcb->asoc.local_scope; + site_scope = stcb->asoc.site_scope; + } else { + /* use generic values for endpoints */ + loopback_scope = 1; + ipv4_local_scope = 1; + local_scope = 1; + site_scope = 1; + } + + /* use only address families of interest */ + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ipv6_addr_legal = 1; + if (SCTP_IPV6_V6ONLY(inp)) { + ipv4_addr_legal = 0; + } else { + ipv4_addr_legal = 1; + } + } else { + ipv4_addr_legal = 1; + ipv6_addr_legal = 0; + } + + error = 0; + + /* neither Mac OS X nor FreeBSD support mulitple routing functions */ + if ((vrf = sctp_find_vrf(inp->def_vrf_id)) == NULL) { + return (-1); + } + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + if ((loopback_scope == 0) && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) + /* Skip loopback if loopback_scope not set */ + continue; + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if (stcb) { + /* + * ignore if blacklisted at + * association level + */ + if (sctp_is_addr_restricted(stcb, sctp_ifa)) + continue; + } + if ((sctp_ifa->address.sa.sa_family == AF_INET) && (ipv4_addr_legal)) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&sctp_ifa->address.sa; + if (sin->sin_addr.s_addr == 0) + continue; + if ((ipv4_local_scope == 0) && (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) + continue; + } else if ((sctp_ifa->address.sa.sa_family == AF_INET6) && (ipv6_addr_legal)) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)&sctp_ifa->address.sa; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) + continue; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + if (local_scope == 0) + continue; + if (sin6->sin6_scope_id == 0) { + /* + * bad link local + * address + */ + if (sa6_recoverscope(sin6) != 0) + continue; + } + } + if ((site_scope == 0) && (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) + continue; + } else + continue; + memset((void *)&xladdr, 0, sizeof(union sctp_sockstore)); + memcpy((void *)&xladdr.address, (const void *)&sctp_ifa->address, sizeof(union sctp_sockstore)); + (void)SCTP_GETTIME_TIMEVAL(&xladdr.start_time); + SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); + error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); + if (error) + return (error); + else { + SCTP_INP_INFO_RLOCK(); + SCTP_INP_RLOCK(inp); + } + } + } + } else { + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + /* ignore if blacklisted at association level */ + if (stcb && sctp_is_addr_restricted(stcb, laddr->ifa)) + continue; + memset((void *)&xladdr, 0, sizeof(union sctp_sockstore)); + memcpy((void *)&xladdr.address, (const void *)&laddr->ifa->address, sizeof(union sctp_sockstore)); + xladdr.start_time = laddr->start_time; + SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); + error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); + if (error) + return (error); + else { + SCTP_INP_INFO_RLOCK(); + SCTP_INP_RLOCK(inp); + } + } + } + memset((void *)&xladdr, 0, sizeof(union sctp_sockstore)); + xladdr.last = 1; + error = SYSCTL_OUT(req, &xladdr, sizeof(struct xsctp_laddr)); + if (error) + return (error); + else + return (0); +} + /* * sysctl functions */ @@ -127,11 +295,8 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) struct sctp_inpcb *inp; struct sctp_tcb *stcb; struct sctp_nets *net; - struct sctp_laddr *laddr; struct xsctp_inpcb xinpcb; struct xsctp_tcb xstcb; - -/* struct xsctp_laddr xladdr; */ struct xsctp_raddr xraddr; number_of_endpoints = 0; @@ -144,12 +309,10 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) { SCTP_INP_RLOCK(inp); number_of_endpoints++; - /* FIXME MT */ - LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { - number_of_local_addresses++; - } + number_of_local_addresses += number_of_addresses(inp); LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { number_of_associations++; + number_of_local_addresses += number_of_addresses(inp); TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { number_of_remote_addresses++; } @@ -158,14 +321,10 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) } SCTP_INP_INFO_RUNLOCK(); n = (number_of_endpoints + 1) * sizeof(struct xsctp_inpcb) + - number_of_local_addresses * sizeof(struct xsctp_laddr) + - number_of_associations * sizeof(struct xsctp_tcb) + - number_of_remote_addresses * sizeof(struct xsctp_raddr); -#ifdef SCTP_DEBUG - printf("inps = %u, stcbs = %u, laddrs = %u, raddrs = %u\n", - number_of_endpoints, number_of_associations, - number_of_local_addresses, number_of_remote_addresses); -#endif + (number_of_local_addresses + number_of_endpoints + number_of_associations) * sizeof(struct xsctp_laddr) + + (number_of_associations + number_of_endpoints) * sizeof(struct xsctp_tcb) + + (number_of_remote_addresses + number_of_associations) * sizeof(struct xsctp_raddr); + /* request some more memory than needed */ req->oldidx = (n + n / 8); return 0; @@ -176,78 +335,53 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) } LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) { SCTP_INP_RLOCK(inp); - number_of_local_addresses = 0; - number_of_associations = 0; - /* - * LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) - * { number_of_local_addresses++; } - */ - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - number_of_associations++; - } xinpcb.last = 0; xinpcb.local_port = ntohs(inp->sctp_lport); - xinpcb.number_local_addresses = number_of_local_addresses; - xinpcb.number_associations = number_of_associations; xinpcb.flags = inp->sctp_flags; xinpcb.features = inp->sctp_features; xinpcb.total_sends = inp->total_sends; xinpcb.total_recvs = inp->total_recvs; xinpcb.total_nospaces = inp->total_nospaces; xinpcb.fragmentation_point = inp->sctp_frag_point; - if (inp->sctp_socket != NULL) { - sotoxsocket(inp->sctp_socket, &xinpcb.xsocket); - } else { - bzero(&xinpcb.xsocket, sizeof xinpcb.xsocket); - xinpcb.xsocket.xso_protocol = IPPROTO_SCTP; - } + xinpcb.qlen = inp->sctp_socket->so_qlen; + xinpcb.maxqlen = inp->sctp_socket->so_qlimit; SCTP_INP_INCR_REF(inp); SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xinpcb, sizeof(struct xsctp_inpcb)); if (error) { + SCTP_INP_DECR_REF(inp); return error; } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); - /* FIXME MT */ - /* - * LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) - * { error = SYSCTL_OUT(req, &xladdr, sizeof(struct - * xsctp_laddr)); if (error) { #if - * defined(SCTP_PER_SOCKET_LOCKING) - * SCTP_SOCKET_UNLOCK(SCTP_INP_SO(inp), 1); - * SCTP_UNLOCK_SHARED(sctppcbinfo.ipi_ep_mtx); #endif - * SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); return - * error; } } - */ + error = copy_out_local_addresses(inp, NULL, req); + if (error) { + SCTP_INP_DECR_REF(inp); + return error; + } LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { SCTP_TCB_LOCK(stcb); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); - number_of_local_addresses = 0; - number_of_remote_addresses = 0; - TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - number_of_remote_addresses++; - } - xstcb.LocalPort = ntohs(inp->sctp_lport); - xstcb.RemPort = ntohs(stcb->rport); + xstcb.last = 0; + xstcb.local_port = ntohs(inp->sctp_lport); + xstcb.remote_port = ntohs(stcb->rport); if (stcb->asoc.primary_destination != NULL) - xstcb.RemPrimAddr = stcb->asoc.primary_destination->ro._l_addr; - xstcb.HeartBeatInterval = stcb->asoc.heart_beat_delay; - xstcb.State = SCTP_GET_STATE(&stcb->asoc); /* FIXME */ - xstcb.InStreams = stcb->asoc.streamincnt; - xstcb.OutStreams = stcb->asoc.streamoutcnt; - xstcb.MaxRetr = stcb->asoc.overall_error_count; - xstcb.PrimProcess = 0; /* not really supported yet */ - xstcb.T1expireds = stcb->asoc.timoinit + stcb->asoc.timocookie; - xstcb.T2expireds = stcb->asoc.timoshutdown + stcb->asoc.timoshutdownack; - xstcb.RtxChunks = stcb->asoc.marked_retrans; - xstcb.StartTime = stcb->asoc.start_time; - xstcb.DiscontinuityTime = stcb->asoc.discontinuity_time; + xstcb.primary_addr = stcb->asoc.primary_destination->ro._l_addr; + xstcb.heartbeat_interval = stcb->asoc.heart_beat_delay; + xstcb.state = SCTP_GET_STATE(&stcb->asoc); /* FIXME */ + xstcb.in_streams = stcb->asoc.streamincnt; + xstcb.out_streams = stcb->asoc.streamoutcnt; + xstcb.max_nr_retrans = stcb->asoc.overall_error_count; + xstcb.primary_process = 0; /* not really supported + * yet */ + xstcb.T1_expireries = stcb->asoc.timoinit + stcb->asoc.timocookie; + xstcb.T2_expireries = stcb->asoc.timoshutdown + stcb->asoc.timoshutdownack; + xstcb.retransmitted_tsns = stcb->asoc.marked_retrans; + xstcb.start_time = stcb->asoc.start_time; + xstcb.discontinuity_time = stcb->asoc.discontinuity_time; - xstcb.number_local_addresses = number_of_local_addresses; - xstcb.number_remote_addresses = number_of_remote_addresses; xstcb.total_sends = stcb->total_sends; xstcb.total_recvs = stcb->total_recvs; xstcb.local_tag = stcb->asoc.my_vtag; @@ -261,43 +395,71 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xstcb, sizeof(struct xsctp_tcb)); if (error) { + SCTP_INP_DECR_REF(inp); + atomic_add_int(&stcb->asoc.refcnt, -1); + return error; + } + SCTP_INP_INFO_RLOCK(); + SCTP_INP_RLOCK(inp); + error = copy_out_local_addresses(inp, stcb, req); + if (error) { + SCTP_INP_DECR_REF(inp); atomic_add_int(&stcb->asoc.refcnt, -1); return error; } TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - xraddr.RemAddr = net->ro._l_addr; - xraddr.RemAddrActive = ((net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE); - xraddr.RemAddrConfirmed = ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0); - xraddr.RemAddrHBActive = ((net->dest_state & SCTP_ADDR_NOHB) == 0); - xraddr.RemAddrRTO = net->RTO; - xraddr.RemAddrMaxPathRtx = net->failure_threshold; - xraddr.RemAddrRtx = net->marked_retrans; - xraddr.RemAddrErrorCounter = net->error_count; - xraddr.RemAddrCwnd = net->cwnd; - xraddr.RemAddrFlightSize = net->flight_size; - xraddr.RemAddrStartTime = net->start_time; - xraddr.RemAddrMTU = net->mtu; + xraddr.last = 0; + xraddr.address = net->ro._l_addr; + xraddr.active = ((net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE); + xraddr.confirmed = ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0); + xraddr.heartbeat_enabled = ((net->dest_state & SCTP_ADDR_NOHB) == 0); + xraddr.rto = net->RTO; + xraddr.max_path_rtx = net->failure_threshold; + xraddr.rtx = net->marked_retrans; + xraddr.error_counter = net->error_count; + xraddr.cwnd = net->cwnd; + xraddr.flight_size = net->flight_size; + xraddr.mtu = net->mtu; + xraddr.start_time = net->start_time; + SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xraddr, sizeof(struct xsctp_raddr)); if (error) { + SCTP_INP_DECR_REF(inp); atomic_add_int(&stcb->asoc.refcnt, -1); return error; } + SCTP_INP_INFO_RLOCK(); + SCTP_INP_RLOCK(inp); } atomic_add_int(&stcb->asoc.refcnt, -1); + memset((void *)&xraddr, 0, sizeof(struct xsctp_raddr)); + xraddr.last = 1; + SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); + error = SYSCTL_OUT(req, &xraddr, sizeof(struct xsctp_raddr)); + if (error) { + SCTP_INP_DECR_REF(inp); + return error; + } SCTP_INP_INFO_RLOCK(); SCTP_INP_RLOCK(inp); } - SCTP_INP_DECR_REF(inp); SCTP_INP_RUNLOCK(inp); + SCTP_INP_INFO_RUNLOCK(); + memset((void *)&xstcb, 0, sizeof(struct xsctp_tcb)); + xstcb.last = 1; + error = SYSCTL_OUT(req, &xstcb, sizeof(struct xsctp_tcb)); + if (error) { + return error; + } + SCTP_INP_INFO_RLOCK(); + SCTP_INP_DECR_REF(inp); } SCTP_INP_INFO_RUNLOCK(); + memset((void *)&xinpcb, 0, sizeof(struct xsctp_inpcb)); xinpcb.last = 1; - xinpcb.local_port = 0; - xinpcb.number_local_addresses = 0; - xinpcb.number_associations = 0; - xinpcb.flags = 0; - xinpcb.features = 0; error = SYSCTL_OUT(req, &xinpcb, sizeof(struct xsctp_inpcb)); return error; } diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index d00ff7975c30..085cea5e6abe 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -1411,7 +1411,7 @@ sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * this will send out extra hb's up to maxburst if there are * any unconfirmed addresses. */ - int cnt_sent = 0; + uint32_t cnt_sent = 0; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) && diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index f3e0836fa0fd..958cc3fca424 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include typedef uint32_t sctp_assoc_t; @@ -413,8 +412,6 @@ struct sctp_paddrparams { #define SPP_HB_DEMAND 0x00000004 #define SPP_PMTUD_ENABLE 0x00000008 #define SPP_PMTUD_DISABLE 0x00000010 -#define SPP_SACKDELAY_ENABLE 0x00000020 -#define SPP_SACKDELAY_DISABLE 0x00000040 #define SPP_HB_TIME_IS_ZERO 0x00000080 #define SPP_IPV6_FLOWLABEL 0x00000100 #define SPP_IPV4_TOS 0x00000200 @@ -443,8 +440,6 @@ struct sctp_assocparams { uint32_t sasoc_peer_rwnd; uint32_t sasoc_local_rwnd; uint32_t sasoc_cookie_life; - uint32_t sasoc_sack_delay; - uint32_t sasoc_sack_freq; }; struct sctp_setprim { @@ -533,6 +528,12 @@ struct sctp_assoc_ids { sctp_assoc_t gaids_assoc_id[0]; }; +struct sctp_sack_info { + sctp_assoc_t sack_assoc_id; + uint32_t sack_delay; + uint32_t sack_freq; +}; + struct sctp_cwnd_args { struct sctp_nets *net; /* network to */ uint32_t cwnd_new_value;/* cwnd in k */ @@ -923,33 +924,33 @@ union sctp_sockstore { struct xsctp_inpcb { uint32_t last; uint16_t local_port; - uint16_t number_local_addresses; - uint32_t number_associations; uint32_t flags; uint32_t features; uint32_t total_sends; uint32_t total_recvs; uint32_t total_nospaces; uint32_t fragmentation_point; - struct xsocket xsocket; + uint16_t qlen; + uint16_t maxqlen; /* add more endpoint specific data here */ }; struct xsctp_tcb { - uint16_t LocalPort; /* sctpAssocEntry 3 */ - uint16_t RemPort; /* sctpAssocEntry 4 */ - union sctp_sockstore RemPrimAddr; /* sctpAssocEntry 5/6 */ - uint32_t HeartBeatInterval; /* sctpAssocEntry 7 */ - uint32_t State; /* sctpAssocEntry 8 */ - uint32_t InStreams; /* sctpAssocEntry 9 */ - uint32_t OutStreams; /* sctpAssocEntry 10 */ - uint32_t MaxRetr; /* sctpAssocEntry 11 */ - uint32_t PrimProcess; /* sctpAssocEntry 12 */ - uint32_t T1expireds; /* sctpAssocEntry 13 */ - uint32_t T2expireds; /* sctpAssocEntry 14 */ - uint32_t RtxChunks; /* sctpAssocEntry 15 */ - struct timeval StartTime; /* sctpAssocEntry 16 */ - struct timeval DiscontinuityTime; /* sctpAssocEntry 17 */ + uint32_t last; + uint16_t local_port; /* sctpAssocEntry 3 */ + uint16_t remote_port; /* sctpAssocEntry 4 */ + union sctp_sockstore primary_addr; /* sctpAssocEntry 5/6 */ + uint32_t heartbeat_interval; /* sctpAssocEntry 7 */ + uint32_t state; /* sctpAssocEntry 8 */ + uint32_t in_streams; /* sctpAssocEntry 9 */ + uint32_t out_streams; /* sctpAssocEntry 10 */ + uint32_t max_nr_retrans;/* sctpAssocEntry 11 */ + uint32_t primary_process; /* sctpAssocEntry 12 */ + uint32_t T1_expireries; /* sctpAssocEntry 13 */ + uint32_t T2_expireries; /* sctpAssocEntry 14 */ + uint32_t retransmitted_tsns; /* sctpAssocEntry 15 */ + struct timeval start_time; /* sctpAssocEntry 16 */ + struct timeval discontinuity_time; /* sctpAssocEntry 17 */ uint32_t total_sends; uint32_t total_recvs; uint32_t local_tag; @@ -960,29 +961,29 @@ struct xsctp_tcb { uint32_t cumulative_tsn_ack; uint32_t mtu; /* add more association specific data here */ - uint16_t number_local_addresses; - uint16_t number_remote_addresses; }; struct xsctp_laddr { - union sctp_sockstore LocalAddr; /* sctpAssocLocalAddrEntry 1/2 */ - struct timeval LocalStartTime; /* sctpAssocLocalAddrEntry 3 */ + uint32_t last; + union sctp_sockstore address; /* sctpAssocLocalAddrEntry 1/2 */ + struct timeval start_time; /* sctpAssocLocalAddrEntry 3 */ /* add more local address specific data */ }; struct xsctp_raddr { - union sctp_sockstore RemAddr; /* sctpAssocLocalRemEntry 1/2 */ - uint8_t RemAddrActive; /* sctpAssocLocalRemEntry 3 */ - uint8_t RemAddrConfirmed; /* */ - uint8_t RemAddrHBActive;/* sctpAssocLocalRemEntry 4 */ - uint32_t RemAddrRTO; /* sctpAssocLocalRemEntry 5 */ - uint32_t RemAddrMaxPathRtx; /* sctpAssocLocalRemEntry 6 */ - uint32_t RemAddrRtx; /* sctpAssocLocalRemEntry 7 */ - uint32_t RemAddrErrorCounter; /* */ - uint32_t RemAddrCwnd; /* */ - uint32_t RemAddrFlightSize; /* */ - uint32_t RemAddrMTU; /* */ - struct timeval RemAddrStartTime; /* sctpAssocLocalRemEntry 8 */ + uint32_t last; + union sctp_sockstore address; /* sctpAssocLocalRemEntry 1/2 */ + uint8_t active; /* sctpAssocLocalRemEntry 3 */ + uint8_t confirmed; /* */ + uint8_t heartbeat_enabled; /* sctpAssocLocalRemEntry 4 */ + uint32_t rto; /* sctpAssocLocalRemEntry 5 */ + uint32_t max_path_rtx; /* sctpAssocLocalRemEntry 6 */ + uint32_t rtx; /* sctpAssocLocalRemEntry 7 */ + uint32_t error_counter; /* */ + uint32_t cwnd; /* */ + uint32_t flight_size; /* */ + uint32_t mtu; /* */ + struct timeval start_time; /* sctpAssocLocalRemEntry 8 */ /* add more remote address specific data */ }; diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index b38a28b5ceab..597705cc867b 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -67,7 +67,7 @@ sctp_init(void) */ sb_max_adj = (u_long)((u_quad_t) (SB_MAX) * MCLBYTES / (MSIZE + MCLBYTES)); sctp_sendspace = min((min(SB_MAX, sb_max_adj)), - ((nmbclusters / 2) * SCTP_DEFAULT_MAXSEGMENT)); + (((uint32_t) nmbclusters / 2) * SCTP_DEFAULT_MAXSEGMENT)); /* * Now for the recv window, should we take the same amount? or * should I do 1/2 the SB_MAX instead in the SB_MAX min above. For @@ -546,7 +546,9 @@ sctp_bind(struct socket *so, struct sockaddr *addr, struct thread *p) /* must be a v4 address! */ return EINVAL; #endif /* INET6 */ - + if (addr && (addr->sa_len != sizeof(struct sockaddr_in))) { + return EINVAL; + } inp = (struct sctp_inpcb *)so->so_pcb; if (inp == 0) return EINVAL; @@ -1256,6 +1258,7 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr; int added = 0; uint32_t vrf_id; + int bad_addresses = 0; sctp_assoc_t *a_id; SCTPDBG(SCTP_DEBUG_PCB1, "Connectx called\n"); @@ -1287,13 +1290,14 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, totaddrp = (int *)optval; totaddr = *totaddrp; sa = (struct sockaddr *)(totaddrp + 1); - stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (optsize - sizeof(int))); - if (stcb != NULL) { + stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (optsize - sizeof(int)), &bad_addresses); + if ((stcb != NULL) || bad_addresses) { /* Already have or am bring up an association */ SCTP_ASOC_CREATE_UNLOCK(inp); creat_lock_on = 0; SCTP_TCB_UNLOCK(stcb); - error = EALREADY; + if (bad_addresses == 0) + error = EALREADY; goto out_now; } #ifdef INET6 @@ -1341,8 +1345,13 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, else sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6)); + error = 0; added = sctp_connectx_helper_add(stcb, sa, (totaddr - 1), &error); /* Fill in the return id */ + if (error) { + sctp_free_assoc(inp, stcb, SCTP_PCBFREE_FORCE, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_12); + goto out_now; + } a_id = (sctp_assoc_t *) optval; *a_id = sctp_get_associd(stcb); @@ -1594,10 +1603,10 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, break; case SCTP_VRF_ID: { - uint32_t *vrf_id; + uint32_t *default_vrfid; - SCTP_CHECK_AND_CAST(vrf_id, optval, uint32_t, *optsize); - *vrf_id = inp->def_vrf_id; + SCTP_CHECK_AND_CAST(default_vrfid, optval, uint32_t, *optsize); + *default_vrfid = inp->def_vrf_id; break; } case SCTP_GET_ASOC_VRF: @@ -1635,22 +1644,23 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, *optsize = sizeof(*gnv); } break; - case SCTP_DELAYED_ACK_TIME: + case SCTP_DELAYED_SACK: { - struct sctp_assoc_value *tm; - - SCTP_CHECK_AND_CAST(tm, optval, struct sctp_assoc_value, *optsize); - SCTP_FIND_STCB(inp, stcb, tm->assoc_id); + struct sctp_sack_info *sack; + SCTP_CHECK_AND_CAST(sack, optval, struct sctp_sack_info, *optsize); + SCTP_FIND_STCB(inp, stcb, sack->sack_assoc_id); if (stcb) { - tm->assoc_value = stcb->asoc.delayed_ack; + sack->sack_delay = stcb->asoc.delayed_ack; + sack->sack_freq = stcb->asoc.sack_freq; SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_RLOCK(inp); - tm->assoc_value = TICKS_TO_MSEC(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]); + sack->sack_delay = TICKS_TO_MSEC(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]); + sack->sack_freq = inp->sctp_ep.sctp_sack_freq; SCTP_INP_RUNLOCK(inp); } - *optsize = sizeof(*tm); + *optsize = sizeof(*sack); } break; @@ -1672,7 +1682,7 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, *optsize = sizeof(struct sctp_sockstat); } break; - case SCTP_MAXBURST: + case SCTP_MAX_BURST: { uint8_t *value; @@ -1690,11 +1700,7 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, int ovh; SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, *optsize); - if (av->assoc_id) { - SCTP_FIND_STCB(inp, stcb, av->assoc_id); - } else { - stcb = NULL; - } + SCTP_FIND_STCB(inp, stcb, av->assoc_id); if (stcb) { av->assoc_value = sctp_get_frag_point(stcb, &stcb->asoc); @@ -1706,7 +1712,10 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, } else { ovh = SCTP_MED_V4_OVERHEAD; } - av->assoc_value = inp->sctp_frag_point - ovh; + if (inp->sctp_frag_point >= SCTP_DEFAULT_MAXSEGMENT) + av->assoc_value = 0; + else + av->assoc_value = inp->sctp_frag_point - ovh; SCTP_INP_RUNLOCK(inp); } *optsize = sizeof(struct sctp_assoc_value); @@ -1927,8 +1936,17 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, /* Applys to the specific association */ paddrp->spp_flags = 0; if (net) { + int ovh; + + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ovh = SCTP_MED_OVERHEAD; + } else { + ovh = SCTP_MED_V4_OVERHEAD; + } + + paddrp->spp_pathmaxrxt = net->failure_threshold; - paddrp->spp_pathmtu = net->mtu; + paddrp->spp_pathmtu = net->mtu - ovh; /* get flags for HB */ if (net->dest_state & SCTP_ADDR_NOHB) paddrp->spp_flags |= SPP_HB_DISABLE; @@ -1957,6 +1975,8 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, * No destination so return default * value */ + int cnt = 0; + paddrp->spp_pathmaxrxt = stcb->asoc.def_net_failure; paddrp->spp_pathmtu = sctp_get_frag_point(stcb, &stcb->asoc); #ifdef INET @@ -1968,8 +1988,18 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, paddrp->spp_flags |= SPP_IPV6_FLOWLABEL; #endif /* default settings should be these */ - if (sctp_is_hb_timer_running(stcb)) { + if (stcb->asoc.hb_is_disabled == 0) { paddrp->spp_flags |= SPP_HB_ENABLE; + } else { + paddrp->spp_flags |= SPP_HB_DISABLE; + } + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { + cnt++; + } + } + if (cnt) { + paddrp->spp_flags |= SPP_PMTUD_ENABLE; } } paddrp->spp_hbinterval = stcb->asoc.heart_beat_delay; @@ -1993,11 +2023,16 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, } #endif /* can't return this */ - paddrp->spp_pathmaxrxt = 0; paddrp->spp_pathmtu = 0; - /* default behavior, no stcb */ - paddrp->spp_flags = SPP_HB_ENABLE | SPP_PMTUD_ENABLE; + /* default behavior, no stcb */ + paddrp->spp_flags = SPP_PMTUD_ENABLE; + + if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) { + paddrp->spp_flags |= SPP_HB_ENABLE; + } else { + paddrp->spp_flags |= SPP_HB_DISABLE; + } SCTP_INP_RUNLOCK(inp); } *optsize = sizeof(struct sctp_paddrparams); @@ -2140,8 +2175,6 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, sasoc->sasoc_peer_rwnd = stcb->asoc.peers_rwnd; sasoc->sasoc_local_rwnd = stcb->asoc.my_rwnd; sasoc->sasoc_cookie_life = TICKS_TO_MSEC(stcb->asoc.cookie_life); - sasoc->sasoc_sack_delay = stcb->asoc.delayed_ack; - sasoc->sasoc_sack_freq = stcb->asoc.sack_freq; SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_RLOCK(inp); @@ -2150,8 +2183,6 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, sasoc->sasoc_peer_rwnd = 0; sasoc->sasoc_local_rwnd = sbspace(&inp->sctp_socket->so_rcv); sasoc->sasoc_cookie_life = TICKS_TO_MSEC(inp->sctp_ep.def_cookie_life); - sasoc->sasoc_sack_delay = TICKS_TO_MSEC(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]); - sasoc->sasoc_sack_freq = inp->sctp_ep.sctp_sack_freq; SCTP_INP_RUNLOCK(inp); } *optsize = sizeof(*sasoc); @@ -2165,11 +2196,11 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, SCTP_FIND_STCB(inp, stcb, s_info->sinfo_assoc_id); if (stcb) { - *s_info = stcb->asoc.def_send; + memcpy(s_info, &stcb->asoc.def_send, sizeof(stcb->asoc.def_send)); SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_RLOCK(inp); - *s_info = inp->def_send; + memcpy(s_info, &inp->def_send, sizeof(inp->def_send)); SCTP_INP_RUNLOCK(inp); } *optsize = sizeof(*s_info); @@ -2199,8 +2230,15 @@ sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, if (stcb) { /* simply copy out the sockaddr_storage... */ - memcpy(&ssp->ssp_addr, &stcb->asoc.primary_destination->ro._l_addr, - ((struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr)->sa_len); + int len; + + len = *optsize; + if (len > stcb->asoc.primary_destination->ro._l_addr.sa.sa_len) + len = stcb->asoc.primary_destination->ro._l_addr.sa.sa_len; + + memcpy(&ssp->ssp_addr, + &stcb->asoc.primary_destination->ro._l_addr, + len); SCTP_TCB_UNLOCK(stcb); } else { error = EINVAL; @@ -2395,6 +2433,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, set_opt = SCTP_PCB_FLAGS_NODELAY; break; case SCTP_AUTOCLOSE: + if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || + (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { + return (EINVAL); + } set_opt = SCTP_PCB_FLAGS_AUTOCLOSE; /* * The value is in ticks. Note this does not effect @@ -2436,7 +2478,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, sctp_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); sctp_feature_off(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS); } else if (*level == SCTP_FRAG_LEVEL_0) { - sctp_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); + sctp_feature_off(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE); sctp_feature_off(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS); } else { @@ -2488,14 +2530,14 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, break; case SCTP_VRF_ID: { - uint32_t *vrf_id; + uint32_t *default_vrfid; - SCTP_CHECK_AND_CAST(vrf_id, optval, uint32_t, optsize); - if (*vrf_id > SCTP_MAX_VRF_ID) { + SCTP_CHECK_AND_CAST(default_vrfid, optval, uint32_t, optsize); + if (*default_vrfid > SCTP_MAX_VRF_ID) { error = EINVAL; break; } - inp->def_vrf_id = *vrf_id; + inp->def_vrf_id = *default_vrfid; break; } case SCTP_DEL_VRF_ID: @@ -2508,20 +2550,34 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EOPNOTSUPP; break; } - - case SCTP_DELAYED_ACK_TIME: + case SCTP_DELAYED_SACK: { - struct sctp_assoc_value *tm; - - SCTP_CHECK_AND_CAST(tm, optval, struct sctp_assoc_value, optsize); - SCTP_FIND_STCB(inp, stcb, tm->assoc_id); + struct sctp_sack_info *sack; + SCTP_CHECK_AND_CAST(sack, optval, struct sctp_sack_info, optsize); + SCTP_FIND_STCB(inp, stcb, sack->sack_assoc_id); if (stcb) { - stcb->asoc.delayed_ack = tm->assoc_value; + if (sack->sack_delay) { + if (MSEC_TO_TICKS(sack->sack_delay) < 1) { + sack->sack_delay = TICKS_TO_MSEC(1); + } + stcb->asoc.delayed_ack = sack->sack_delay; + } + if (sack->sack_freq) { + stcb->asoc.sack_freq = sack->sack_freq; + } SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_WLOCK(inp); - inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(tm->assoc_value); + if (sack->sack_delay) { + if (MSEC_TO_TICKS(sack->sack_delay) < 1) { + sack->sack_delay = TICKS_TO_MSEC(1); + } + inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(sack->sack_delay); + } + if (sack->sack_freq) { + inp->sctp_ep.sctp_sack_freq = sack->sack_freq; + } SCTP_INP_WUNLOCK(inp); } break; @@ -2618,7 +2674,7 @@ 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; + size_t size, i, found; SCTP_CHECK_AND_CAST(shmac, optval, struct sctp_hmacalgo, optsize); size = (optsize - sizeof(*shmac)) / sizeof(shmac->shmac_idents[0]); @@ -2636,6 +2692,18 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, goto sctp_set_hmac_done; } } + found = 0; + for (i = 0; i < hmaclist->num_algo; i++) { + if (hmaclist->hmac[i] == SCTP_AUTH_HMAC_ID_SHA1) { + /* already in list */ + found = 1; + } + } + if (!found) { + sctp_free_hmaclist(hmaclist); + error = EINVAL; + break; + } /* set it on the endpoint */ SCTP_INP_WLOCK(inp); if (inp->sctp_ep.local_hmacs) @@ -2828,7 +2896,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_TCB_UNLOCK(stcb); } break; - case SCTP_MAXBURST: + case SCTP_MAX_BURST: { uint8_t *burst; @@ -2849,16 +2917,20 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, optsize); SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ovh = SCTP_MED_OVERHEAD; + } else { + ovh = SCTP_MED_V4_OVERHEAD; + } if (stcb) { - error = EINVAL; + if (av->assoc_value) { + stcb->asoc.sctp_frag_point = (av->assoc_value + ovh); + } else { + stcb->asoc.sctp_frag_point = SCTP_DEFAULT_MAXSEGMENT; + } SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_WLOCK(inp); - if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { - ovh = SCTP_MED_OVERHEAD; - } else { - ovh = SCTP_MED_V4_OVERHEAD; - } /* * FIXME MT: I think this is not in tune * with the API ID @@ -2866,7 +2938,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (av->assoc_value) { inp->sctp_frag_point = (av->assoc_value + ovh); } else { - error = EINVAL; + inp->sctp_frag_point = SCTP_DEFAULT_MAXSEGMENT; } SCTP_INP_WUNLOCK(inp); } @@ -2973,14 +3045,14 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (stcb) { if (s_info->sinfo_stream <= stcb->asoc.streamoutcnt) { - stcb->asoc.def_send = *s_info; + memcpy(&stcb->asoc.def_send, s_info, min(optsize, sizeof(stcb->asoc.def_send))); } else { error = EINVAL; } SCTP_TCB_UNLOCK(stcb); } else { SCTP_INP_WLOCK(inp); - inp->def_send = *s_info; + memcpy(&inp->def_send, s_info, min(optsize, sizeof(inp->def_send))); SCTP_INP_WUNLOCK(inp); } } @@ -3012,14 +3084,31 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_INP_DECR_REF(inp); } } - - + /* sanity checks */ + if ((paddrp->spp_flags & SPP_HB_ENABLE) && (paddrp->spp_flags & SPP_HB_DISABLE)) { + if (stcb) + SCTP_TCB_UNLOCK(stcb); + return (EINVAL); + } + if ((paddrp->spp_flags & SPP_PMTUD_ENABLE) && (paddrp->spp_flags & SPP_PMTUD_DISABLE)) { + if (stcb) + SCTP_TCB_UNLOCK(stcb); + return (EINVAL); + } if (stcb) { /************************TCB SPECIFIC SET ******************/ /* * do we change the timer for HB, we run * only one? */ + int ovh = 0; + + if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { + ovh = SCTP_MED_OVERHEAD; + } else { + ovh = SCTP_MED_V4_OVERHEAD; + } + if (paddrp->spp_hbinterval) stcb->asoc.heart_beat_delay = paddrp->spp_hbinterval; else if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) @@ -3038,13 +3127,13 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (paddrp->spp_flags & SPP_HB_ENABLE) { net->dest_state &= ~SCTP_ADDR_NOHB; } - if (paddrp->spp_flags & SPP_PMTUD_DISABLE) { + if ((paddrp->spp_flags & SPP_PMTUD_DISABLE) && (paddrp->spp_pathmtu >= SCTP_SMALLEST_PMTU)) { if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); } if (paddrp->spp_pathmtu > SCTP_DEFAULT_MINSEGMENT) { - net->mtu = paddrp->spp_pathmtu; + net->mtu = paddrp->spp_pathmtu + ovh; if (net->mtu < stcb->asoc.smallest_mtu) { #ifdef SCTP_PRINT_FOR_B_AND_M SCTP_PRINTF("SCTP_PMTU_DISABLE calls sctp_pathmtu_adjustment:%d\n", @@ -3085,6 +3174,31 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, stcb->asoc.hb_is_disabled = 0; sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); } + if ((paddrp->spp_flags & SPP_PMTUD_DISABLE) && (paddrp->spp_pathmtu >= SCTP_SMALLEST_PMTU)) { + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { + sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net, + SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); + } + if (paddrp->spp_pathmtu > SCTP_DEFAULT_MINSEGMENT) { + net->mtu = paddrp->spp_pathmtu + ovh; + if (net->mtu < stcb->asoc.smallest_mtu) { +#ifdef SCTP_PRINT_FOR_B_AND_M + SCTP_PRINTF("SCTP_PMTU_DISABLE calls sctp_pathmtu_adjustment:%d\n", + net->mtu); +#endif + sctp_pathmtu_adjustment(inp, stcb, net, net->mtu); + } + } + } + } + if (paddrp->spp_flags & SPP_PMTUD_ENABLE) { + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net); + } + } + } if (paddrp->spp_flags & SPP_HB_DISABLE) { int cnt_of_unconf = 0; struct sctp_nets *lnet; @@ -3101,12 +3215,17 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, * addresses */ if (cnt_of_unconf == 0) { - sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_11); + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, + SCTP_FROM_SCTP_USRREQ + SCTP_LOC_11); + } } } if (paddrp->spp_flags & SPP_HB_ENABLE) { /* start up the timer. */ - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + } } #ifdef INET if (paddrp->spp_flags & SPP_IPV4_TOS) @@ -3129,9 +3248,14 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (paddrp->spp_pathmaxrxt) { inp->sctp_ep.def_net_failure = paddrp->spp_pathmaxrxt; } - if (paddrp->spp_flags & SPP_HB_ENABLE) { + if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) + inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = 0; + else if (paddrp->spp_hbinterval) inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = MSEC_TO_TICKS(paddrp->spp_hbinterval); + + if (paddrp->spp_flags & SPP_HB_ENABLE) { sctp_feature_off(inp, SCTP_PCB_FLAGS_DONOT_HEARTBEAT); + } else if (paddrp->spp_flags & SPP_HB_DISABLE) { sctp_feature_on(inp, SCTP_PCB_FLAGS_DONOT_HEARTBEAT); } @@ -3206,11 +3330,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, sasoc->sasoc_number_peer_destinations = stcb->asoc.numnets; sasoc->sasoc_peer_rwnd = 0; sasoc->sasoc_local_rwnd = 0; - if (sasoc->sasoc_cookie_life) + if (sasoc->sasoc_cookie_life) { + if (sasoc->sasoc_cookie_life < 1000) + sasoc->sasoc_cookie_life = 1000; stcb->asoc.cookie_life = MSEC_TO_TICKS(sasoc->sasoc_cookie_life); - stcb->asoc.delayed_ack = sasoc->sasoc_sack_delay; - if (sasoc->sasoc_sack_freq) { - stcb->asoc.sack_freq = sasoc->sasoc_sack_freq; } SCTP_TCB_UNLOCK(stcb); } else { @@ -3220,11 +3343,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, sasoc->sasoc_number_peer_destinations = 0; sasoc->sasoc_peer_rwnd = 0; sasoc->sasoc_local_rwnd = 0; - if (sasoc->sasoc_cookie_life) + if (sasoc->sasoc_cookie_life) { + if (sasoc->sasoc_cookie_life < 1000) + sasoc->sasoc_cookie_life = 1000; inp->sctp_ep.def_cookie_life = MSEC_TO_TICKS(sasoc->sasoc_cookie_life); - inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(sasoc->sasoc_sack_delay); - if (sasoc->sasoc_sack_freq) { - inp->sctp_ep.sctp_sack_freq = sasoc->sasoc_sack_freq; } SCTP_INP_WUNLOCK(inp); } @@ -3320,9 +3442,43 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_CHECK_AND_CAST(sspp, optval, struct sctp_setpeerprim, optsize); SCTP_FIND_STCB(inp, stcb, sspp->sspp_assoc_id); if (stcb != NULL) { - if (sctp_set_primary_ip_address_sa(stcb, (struct sockaddr *)&sspp->sspp_addr) != 0) { + struct sctp_ifa *ifa; + + ifa = sctp_find_ifa_by_addr((struct sockaddr *)&sspp->sspp_addr, + stcb->asoc.vrf_id, 0); + if (ifa == NULL) { + error = EINVAL; + goto out_of_it; + } + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { + /* + * Must validate the ifa found is in + * our ep + */ + struct sctp_laddr *laddr; + int found = 0; + + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa == NULL) { + SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n", + __FUNCTION__); + continue; + } + if (laddr->ifa == ifa) { + found = 1; + break; + } + } + if (!found) { + error = EINVAL; + goto out_of_it; + } + } + if (sctp_set_primary_ip_address_sa(stcb, + (struct sockaddr *)&sspp->sspp_addr) != 0) { error = EINVAL; } + out_of_it: SCTP_TCB_UNLOCK(stcb); } else { error = EINVAL; @@ -3349,6 +3505,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (addrs->addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; + if (addrs->addr->sa_len != sizeof(struct sockaddr_in6)) { + error = EINVAL; + break; + } sin6 = (struct sockaddr_in6 *)addr_touse; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { in6_sin6_2_sin(&sin, sin6); @@ -3356,7 +3516,14 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } } #endif + if (addrs->addr->sa_family == AF_INET) { + if (addrs->addr->sa_len != sizeof(struct sockaddr_in)) { + error = EINVAL; + break; + } + } if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) { + if (p == NULL) { /* Can't get proc for Net/Open BSD */ error = EINVAL; @@ -3423,6 +3590,10 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, if (addrs->addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; + if (addrs->addr->sa_len != sizeof(struct sockaddr_in6)) { + error = EINVAL; + break; + } sin6 = (struct sockaddr_in6 *)addr_touse; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { in6_sin6_2_sin(&sin, sin6); @@ -3430,6 +3601,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } } #endif + if (addrs->addr->sa_family == AF_INET) { + if (addrs->addr->sa_len != sizeof(struct sockaddr_in)) { + error = EINVAL; + break; + } + } /* * No lock required mgmt_ep_sa does its own locking. * If the FIX: below is ever changed we may need to @@ -3525,6 +3702,15 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p) /* I made the same as TCP since we are not setup? */ return (ECONNRESET); } + if (addr == NULL) + return EINVAL; + + if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) { + return (EINVAL); + } + if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) { + return (EINVAL); + } SCTP_ASOC_CREATE_LOCK(inp); create_lock_on = 1; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index f3c685a57933..755887515b66 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -923,6 +923,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_association *asoc, asoc->heart_beat_delay = TICKS_TO_MSEC(m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT]); asoc->cookie_life = m->sctp_ep.def_cookie_life; asoc->sctp_cmt_on_off = (uint8_t) sctp_cmt_on_off; + asoc->sctp_frag_point = m->sctp_frag_point; #ifdef INET asoc->default_tos = m->ip_inp.inp.inp_ip_tos; #else @@ -1453,6 +1454,11 @@ sctp_timeout_handler(void *t) /* call the handler for the appropriate timer type */ switch (tmr->type) { + case SCTP_TIMER_TYPE_ZERO_COPY: + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { + SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); + } + break; case SCTP_TIMER_TYPE_ADDR_WQ: sctp_handle_addr_wq(); break; @@ -1770,6 +1776,10 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, SCTP_TCB_LOCK_ASSERT(stcb); } switch (t_type) { + case SCTP_TIMER_TYPE_ZERO_COPY: + tmr = &inp->sctp_ep.zero_copy_timer; + to_ticks = SCTP_ZERO_COPY_TICK_DELAY; + break; case SCTP_TIMER_TYPE_ADDR_WQ: /* Only 1 tick away :-) */ tmr = &sctppcbinfo.addr_wq_timer; @@ -2117,6 +2127,10 @@ sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, SCTP_TCB_LOCK_ASSERT(stcb); } switch (t_type) { + case SCTP_TIMER_TYPE_ZERO_COPY: + tmr = &inp->sctp_ep.zero_copy_timer; + break; + case SCTP_TIMER_TYPE_ADDR_WQ: tmr = &sctppcbinfo.addr_wq_timer; break; @@ -5214,9 +5228,7 @@ sctp_sorecvmsg(struct socket *so, copied_so_far += cp_len; } } - if ((out_flags & MSG_EOR) || - (uio->uio_resid == 0) - ) { + if ((out_flags & MSG_EOR) || (uio->uio_resid == 0)) { break; } if (((stcb) && (in_flags & MSG_PEEK) == 0) && @@ -5238,8 +5250,7 @@ sctp_sorecvmsg(struct socket *so, * a MSG_EOR/or read all the user wants... * control->length == 0. */ - if ((out_flags & MSG_EOR) && - ((in_flags & MSG_PEEK) == 0)) { + if ((out_flags & MSG_EOR) && ((in_flags & MSG_PEEK) == 0)) { /* we are done with this control */ if (control->length == 0) { if (control->data) { @@ -5592,6 +5603,7 @@ sctp_dynamic_set_primary(struct sockaddr *sa, uint32_t vrf_id) /* Now incr the count and int wi structure */ SCTP_INCR_LADDR_COUNT(); bzero(wi, sizeof(*wi)); + (void)SCTP_GETTIME_TIMEVAL(&wi->start_time); wi->ifa = ifa; wi->action = SCTP_SET_PRIM_ADDR; atomic_add_int(&ifa->refcount, 1); @@ -5739,7 +5751,8 @@ sctp_l_soreceive(struct socket *so, int -sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, int totaddr, int *error) +sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, + int totaddr, int *error) { int added = 0; int i; @@ -5777,8 +5790,9 @@ sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, int totad } struct sctp_tcb * -sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, int *totaddr, - int *num_v4, int *num_v6, int *error, int max) +sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, + int *totaddr, int *num_v4, int *num_v6, int *error, + int limit, int *bad_addr) { struct sockaddr *sa; struct sctp_tcb *stcb = NULL; @@ -5792,6 +5806,11 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, int *to if (sa->sa_family == AF_INET) { (*num_v4) += 1; incr = sizeof(struct sockaddr_in); + if (sa->sa_len != incr) { + *error = EINVAL; + *bad_addr = 1; + return (NULL); + } } else if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; @@ -5799,21 +5818,30 @@ sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, int *to if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { /* Must be non-mapped for connectx */ *error = EINVAL; + *bad_addr = 1; return (NULL); } (*num_v6) += 1; incr = sizeof(struct sockaddr_in6); + if (sa->sa_len != incr) { + *error = EINVAL; + *bad_addr = 1; + return (NULL); + } } else { *totaddr = i; /* we are done */ break; } + SCTP_INP_INCR_REF(inp); stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL); if (stcb != NULL) { /* Already have or am bring up an association */ return (stcb); + } else { + SCTP_INP_DECR_REF(inp); } - if ((at + incr) > max) { + if ((at + incr) > limit) { *totaddr = i; break; } diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index 94acbddb274e..137363876a4a 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -219,11 +219,13 @@ void sctp_handle_ootb(struct mbuf *, int, int, struct sctphdr *, struct sctp_inpcb *, struct mbuf *, uint32_t, uint32_t); -int sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, int totaddr, int *error); +int +sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, + int totaddr, int *error); struct sctp_tcb * -sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, int *totaddr, - int *num_v4, int *num_v6, int *error, int max); +sctp_connectx_helper_find(struct sctp_inpcb *inp, struct sockaddr *addr, + int *totaddr, int *num_v4, int *num_v6, int *error, int limit, int *bad_addr); int sctp_is_there_an_abort_here(struct mbuf *, int, uint32_t *); uint32_t sctp_is_same_scope(struct sockaddr_in6 *, struct sockaddr_in6 *); diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 1a5e4112c321..db1b81a3a4c9 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -76,6 +76,7 @@ sctp6_input(i_pak, offp, proto) int length, mlen, offset, iphlen; uint8_t ecn_bits; struct sctp_tcb *stcb = NULL; + int pkt_len = 0; int off = *offp; /* get the VRF and table id's */ @@ -88,10 +89,12 @@ sctp6_input(i_pak, offp, proto) return (-1); } m = SCTP_HEADER_TO_CHAIN(*i_pak); + pkt_len = SCTP_HEADER_LEN((*i_pak)); ip6 = mtod(m, struct ip6_hdr *); /* Ensure that (sctphdr + sctp_chunkhdr) in a row. */ - IP6_EXTHDR_GET(sh, struct sctphdr *, m, off, sizeof(*sh) + sizeof(*ch)); + IP6_EXTHDR_GET(sh, struct sctphdr *, m, off, + (int)(sizeof(*sh) + sizeof(*ch))); if (sh == NULL) { SCTP_STAT_INCR(sctps_hdrops); return IPPROTO_DONE; @@ -110,7 +113,7 @@ sctp6_input(i_pak, offp, proto) SCTP_STAT_INCR(sctps_recvpackets); SCTP_STAT_INCR_COUNTER64(sctps_inpackets); SCTPDBG(SCTP_DEBUG_INPUT1, "V6 input gets a packet iphlen:%d pktlen:%d\n", - iphlen, SCTP_HEADER_LEN((*i_pak))); + iphlen, pkt_len); if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* No multi-cast support in SCTP */ goto bad; @@ -588,6 +591,16 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p) if (inp == 0) return EINVAL; + if (addr) { + if ((addr->sa_family == AF_INET6) && + (addr->sa_len != sizeof(struct sockaddr_in6))) { + return EINVAL; + } + if ((addr->sa_family == AF_INET) && + (addr->sa_len != sizeof(struct sockaddr_in))) { + return EINVAL; + } + } inp6 = (struct in6pcb *)inp; inp6->inp_vflag &= ~INP_IPV4; inp6->inp_vflag |= INP_IPV6; @@ -938,6 +951,15 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) return (ECONNRESET); /* I made the same as TCP since we are * not setup? */ } + if (addr == NULL) { + return (EINVAL); + } + if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) { + return (EINVAL); + } + if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) { + return (EINVAL); + } vrf_id = inp->def_vrf_id; SCTP_ASOC_CREATE_LOCK(inp); SCTP_INP_RLOCK(inp);