diff options
-rw-r--r-- | sys/netinet/sctp.h | 2 | ||||
-rw-r--r-- | sys/netinet/sctp_asconf.c | 2 | ||||
-rw-r--r-- | sys/netinet/sctp_constants.h | 2 | ||||
-rw-r--r-- | sys/netinet/sctp_indata.c | 182 | ||||
-rw-r--r-- | sys/netinet/sctp_indata.h | 6 | ||||
-rw-r--r-- | sys/netinet/sctp_input.c | 174 | ||||
-rw-r--r-- | sys/netinet/sctp_input.h | 10 | ||||
-rw-r--r-- | sys/netinet/sctp_os_bsd.h | 110 | ||||
-rw-r--r-- | sys/netinet/sctp_output.c | 465 | ||||
-rw-r--r-- | sys/netinet/sctp_output.h | 14 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.c | 239 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.h | 28 | ||||
-rw-r--r-- | sys/netinet/sctp_structs.h | 19 | ||||
-rw-r--r-- | sys/netinet/sctp_sysctl.c | 3 | ||||
-rw-r--r-- | sys/netinet/sctp_timer.c | 65 | ||||
-rw-r--r-- | sys/netinet/sctp_uio.h | 23 | ||||
-rw-r--r-- | sys/netinet/sctp_usrreq.c | 92 | ||||
-rw-r--r-- | sys/netinet/sctp_var.h | 3 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 469 | ||||
-rw-r--r-- | sys/netinet/sctputil.h | 26 | ||||
-rw-r--r-- | sys/netinet6/sctp6_usrreq.c | 43 |
21 files changed, 1250 insertions, 727 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index c89d1c8..b9289c1 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -429,6 +429,8 @@ struct sctp_error_unrecognized_chunk { #define SCTP_PCB_FLAGS_INTERLEAVE_STRMS 0x00000010 #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 diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c index 5eaa7cd..a5a304e 100644 --- a/sys/netinet/sctp_asconf.c +++ b/sys/netinet/sctp_asconf.c @@ -1917,7 +1917,7 @@ sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, struct sctp_nets *net; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - struct rtentry *rt; + sctp_rtentry_t *rt; /* delete this address if cached */ if (net->ro._s_addr && diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 3922222..ba4e4b0 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -727,6 +727,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_INITIAL_CWND 4380 +#define SCTP_DEFAULT_MTU 1500 /* emegency default MTU */ /* amount peer is obligated to have in rwnd or I will abort */ #define SCTP_MIN_RWND 1500 @@ -843,6 +844,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_LOC_30 0x0000001e #define SCTP_LOC_31 0x0000001f #define SCTP_LOC_32 0x00000020 +#define SCTP_LOC_33 0x00000021 /* Free assoc codes */ diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index bf5727a..3f30220 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -207,6 +207,7 @@ sctp_build_readq_entry(struct sctp_tcb *stcb, read_queue_e->data = dm; read_queue_e->spec_flags = 0; read_queue_e->tail_mbuf = NULL; + read_queue_e->aux_data = NULL; read_queue_e->stcb = stcb; read_queue_e->port_from = stcb->rport; read_queue_e->do_not_ref_stcb = 0; @@ -241,6 +242,7 @@ sctp_build_readq_entry_chk(struct sctp_tcb *stcb, read_queue_e->sinfo_cumtsn = chk->rec.data.TSN_seq; read_queue_e->sinfo_assoc_id = sctp_get_associd(stcb); read_queue_e->whoFrom = chk->whoTo; + read_queue_e->aux_data = NULL; read_queue_e->length = 0; atomic_add_int(&chk->whoTo->ref_count, 1); read_queue_e->data = chk->data; @@ -304,6 +306,50 @@ sctp_build_ctl_nchunk(struct sctp_inpcb *inp, } +char * +sctp_build_ctl_cchunk(struct sctp_inpcb *inp, + int *control_len, + struct sctp_sndrcvinfo *sinfo) +{ + struct sctp_sndrcvinfo *outinfo; + struct cmsghdr *cmh; + char *buf; + int len; + int use_extended = 0; + + if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) { + /* user does not want the sndrcv ctl */ + return (NULL); + } + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO)) { + use_extended = 1; + len = CMSG_LEN(sizeof(struct sctp_extrcvinfo)); + } else { + len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + } + SCTP_MALLOC(buf, char *, len, "SCTP_CMSG"); + if (buf == NULL) { + /* No space */ + return (buf); + } + /* We need a CMSG header followed by the struct */ + cmh = (struct cmsghdr *)buf; + outinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmh); + cmh->cmsg_level = IPPROTO_SCTP; + if (use_extended) { + cmh->cmsg_type = SCTP_EXTRCV; + cmh->cmsg_len = len; + memcpy(outinfo, sinfo, len); + } else { + cmh->cmsg_type = SCTP_SNDRCV; + cmh->cmsg_len = len; + *outinfo = *sinfo; + } + *control_len = len; + return (buf); +} + + /* * We are delivering currently from the reassembly queue. We must continue to * deliver until we either: 1) run out of space. 2) run out of sequential @@ -1453,6 +1499,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, int ordered; uint32_t protocol_id; uint8_t chunk_flags; + struct sctp_stream_reset_list *liste; chk = NULL; tsn = ntohl(ch->dp.tsn); @@ -2106,6 +2153,57 @@ finish_express_del: asoc->highest_tsn_inside_map, SCTP_MAP_PREPARE_SLIDE); #endif 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))) + ) { + /* + * we have finished working through the backlogged TSN's now + * time to reset streams. 1: call reset function. 2: free + * pending_reply space 3: distribute any chunks in + * pending_reply_queue. + */ + struct sctp_queued_to_read *ctl; + + sctp_reset_in_stream(stcb, liste->number_entries, liste->req.list_of_streams); + TAILQ_REMOVE(&asoc->resetHead, liste, next_resp); + SCTP_FREE(liste); + liste = TAILQ_FIRST(&asoc->resetHead); + ctl = TAILQ_FIRST(&asoc->pending_reply_queue); + if (ctl && (liste == NULL)) { + /* All can be removed */ + while (ctl) { + TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); + sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag); + if (*abort_flag) { + return (0); + } + ctl = TAILQ_FIRST(&asoc->pending_reply_queue); + } + } else if (ctl) { + /* more than one in queue */ + while (!compare_with_wrap(ctl->sinfo_tsn, ntohl(liste->tsn), MAX_TSN)) { + /* + * if ctl->sinfo_tsn is <= liste->tsn we can + * process it which is the NOT of + * ctl->sinfo_tsn > liste->tsn + */ + TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); + sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag); + if (*abort_flag) { + return (0); + } + ctl = TAILQ_FIRST(&asoc->pending_reply_queue); + } + } + /* + * Now service re-assembly to pick up anything that has been + * held on reassembly queue? + */ + sctp_deliver_reasm_check(stcb, asoc); + need_reasm_check = 0; + } if (need_reasm_check) { /* Another one waits ? */ sctp_deliver_reasm_check(stcb, asoc); @@ -2166,7 +2264,6 @@ sctp_sack_check(struct sctp_tcb *stcb, int ok_to_sack, int was_a_gap, int *abort unsigned char aux_array[64]; #endif - struct sctp_stream_reset_list *liste; asoc = &stcb->asoc; at = 0; @@ -2301,56 +2398,6 @@ sctp_sack_check(struct sctp_tcb *stcb, int ok_to_sack, int was_a_gap, int *abort #endif } } - /* 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))) - ) { - /* - * we have finished working through the backlogged TSN's now - * time to reset streams. 1: call reset function. 2: free - * pending_reply space 3: distribute any chunks in - * pending_reply_queue. - */ - struct sctp_queued_to_read *ctl; - - sctp_reset_in_stream(stcb, liste->number_entries, liste->req.list_of_streams); - TAILQ_REMOVE(&asoc->resetHead, liste, next_resp); - SCTP_FREE(liste); - liste = TAILQ_FIRST(&asoc->resetHead); - ctl = TAILQ_FIRST(&asoc->pending_reply_queue); - if (ctl && (liste == NULL)) { - /* All can be removed */ - while (ctl) { - TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); - sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag); - if (*abort_flag) { - return; - } - ctl = TAILQ_FIRST(&asoc->pending_reply_queue); - } - } else if (ctl) { - /* more than one in queue */ - while (!compare_with_wrap(ctl->sinfo_tsn, ntohl(liste->tsn), MAX_TSN)) { - /* - * if ctl->sinfo_tsn is <= liste->tsn we can - * process it which is the NOT of - * ctl->sinfo_tsn > liste->tsn - */ - TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next); - sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag); - if (*abort_flag) { - return; - } - ctl = TAILQ_FIRST(&asoc->pending_reply_queue); - } - } - /* - * Now service re-assembly to pick up anything that has been - * held on reassembly queue? - */ - sctp_deliver_reasm_check(stcb, asoc); - } /* * Now we need to see if we need to queue a sack or just start the * timer (if allowed). @@ -2609,7 +2656,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length, } stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_19; sctp_abort_association(inp, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); return (2); } #ifdef SCTP_AUDITING_ENABLED @@ -2672,7 +2719,7 @@ sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length, struct mbuf *op_err; op_err = sctp_generate_invmanparam(SCTP_CAUSE_PROTOCOL_VIOLATION); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, 0, 0); return (2); } break; @@ -5713,14 +5760,37 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, gap = new_cum_tsn + (MAX_TSN - asoc->mapping_array_base_tsn) + 1; } - if (gap > m_size || gap < 0) { + if (gap > m_size) { asoc->highest_tsn_inside_map = back_out_htsn; if ((long)gap > sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv)) { + struct mbuf *oper; + /* * out of range (of single byte chunks in the rwnd I - * give out) too questionable. better to drop it - * silently + * give out). This must be an attacker. */ + *abort_flag = 1; + oper = sctp_get_mbuf_for_msg((sizeof(struct sctp_paramhdr) + 3 * sizeof(uint32_t)), + 0, M_DONTWAIT, 1, MT_DATA); + if (oper) { + struct sctp_paramhdr *ph; + uint32_t *ippp; + + SCTP_BUF_LEN(oper) = sizeof(struct sctp_paramhdr) + + (sizeof(uint32_t) * 3); + ph = mtod(oper, struct sctp_paramhdr *); + ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION); + ph->param_length = htons(SCTP_BUF_LEN(oper)); + ippp = (uint32_t *) (ph + 1); + *ippp = htonl(SCTP_FROM_SCTP_INDATA + SCTP_LOC_33); + ippp++; + *ippp = asoc->highest_tsn_inside_map; + ippp++; + *ippp = new_cum_tsn; + } + stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_33; + sctp_abort_an_association(stcb->sctp_ep, stcb, + SCTP_PEER_FAULTY, oper); return; } if (asoc->highest_tsn_inside_map > diff --git a/sys/netinet/sctp_indata.h b/sys/netinet/sctp_indata.h index 70ba477..ad04c56 100644 --- a/sys/netinet/sctp_indata.h +++ b/sys/netinet/sctp_indata.h @@ -64,6 +64,7 @@ sctp_build_readq_entry(struct sctp_tcb *stcb, (_ctl)->whoFrom = net; \ (_ctl)->data = dm; \ (_ctl)->tail_mbuf = NULL; \ + (_ctl)->aux_data = NULL; \ (_ctl)->stcb = (in_it); \ (_ctl)->port_from = (in_it)->rport; \ (_ctl)->spec_flags = 0; \ @@ -80,6 +81,11 @@ struct mbuf * sctp_build_ctl_nchunk(struct sctp_inpcb *inp, struct sctp_sndrcvinfo *sinfo); +char * +sctp_build_ctl_cchunk(struct sctp_inpcb *inp, + int *control_len, + struct sctp_sndrcvinfo *sinfo); + void sctp_set_rwnd(struct sctp_tcb *, struct sctp_association *); uint32_t diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index eae7b79..97ca903 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -78,7 +78,8 @@ sctp_stop_all_cookie_timers(struct sctp_tcb *stcb) static void sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_init_chunk *cp, struct sctp_inpcb *inp, struct sctp_tcb *stcb, - struct sctp_nets *net, int *abort_no_unlock) + struct sctp_nets *net, int *abort_no_unlock, uint32_t vrf_id, + uint32_t table_id) { struct sctp_init *init; struct mbuf *op_err; @@ -102,7 +103,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, * FIX ME ?? What about TCP model and we have a * match/restart case? */ - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); if (stcb) *abort_no_unlock = 1; return; @@ -110,7 +112,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_chunk)) { /* Invalid length */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); if (stcb) *abort_no_unlock = 1; return; @@ -119,7 +122,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, if (init->initiate_tag == 0) { /* protocol error... send abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); if (stcb) *abort_no_unlock = 1; return; @@ -127,13 +131,15 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, if (ntohl(init->a_rwnd) < SCTP_MIN_RWND) { /* invalid parameter... send abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); return; } if (init->num_inbound_streams == 0) { /* protocol error... send abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); if (stcb) *abort_no_unlock = 1; return; @@ -141,7 +147,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, if (init->num_outbound_streams == 0) { /* protocol error... send abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); - sctp_abort_association(inp, stcb, m, iphlen, sh, op_err); + sctp_abort_association(inp, stcb, m, iphlen, sh, op_err, + vrf_id, table_id); if (stcb) *abort_no_unlock = 1; return; @@ -150,7 +157,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, if (sctp_validate_init_auth_params(m, offset + sizeof(*cp), init_limit)) { /* auth parameter(s) error... send abort */ - sctp_abort_association(inp, stcb, m, iphlen, sh, NULL); + sctp_abort_association(inp, stcb, m, iphlen, sh, NULL, vrf_id, + table_id); if (stcb) *abort_no_unlock = 1; return; @@ -161,7 +169,8 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, printf("sctp_handle_init: sending INIT-ACK\n"); } #endif - sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp); + sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp, vrf_id, + table_id); } /* @@ -307,7 +316,8 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, static int sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb, - struct sctp_nets *net, int *abort_no_unlock) + struct sctp_nets *net, int *abort_no_unlock, uint32_t vrf_id, + uint32_t table_id) { struct sctp_association *asoc; struct mbuf *op_err; @@ -324,7 +334,9 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, 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); + 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 @@ -358,7 +370,7 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, } #endif sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - NULL); + NULL, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -417,7 +429,7 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, mp->resv = 0; } sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, - sh, op_err); + sh, op_err, 0, 0); *abort_no_unlock = 1; } return (retval); @@ -920,9 +932,10 @@ sctp_handle_error(struct sctp_chunkhdr *ch, } static int -sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, - struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb, - struct sctp_nets *net, int *abort_no_unlock) +sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, + struct sctphdr *sh, struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb, + struct sctp_nets *net, int *abort_no_unlock, uint32_t vrf_id, + uint32_t table_id) { struct sctp_init_ack *init_ack; int *state; @@ -945,7 +958,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, /* Invalid length */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -955,7 +968,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, /* protocol error... send an abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -963,7 +976,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, /* protocol error... send an abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -971,7 +984,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, /* protocol error... send an abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -979,7 +992,7 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, /* protocol error... send an abort */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh, - op_err); + op_err, 0, 0); *abort_no_unlock = 1; return (-1); } @@ -1002,8 +1015,9 @@ sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, stcb, 0, (void *)stcb->asoc.primary_destination); } - if (sctp_process_init_ack(m, iphlen, offset, sh, cp, stcb, net, - abort_no_unlock) < 0) { + if (sctp_process_init_ack(m, iphlen, offset, sh, cp, stcb, + net, abort_no_unlock, vrf_id, + table_id) < 0) { /* error in parsing parameters */ return (-1); } @@ -1069,7 +1083,8 @@ static struct sctp_tcb * sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net, - struct sockaddr *init_src, int *notification, sctp_assoc_t * sac_assoc_id) + struct sockaddr *init_src, int *notification, sctp_assoc_t * sac_assoc_id, + uint32_t vrf_id, uint32_t table_id) { struct sctp_association *asoc; struct sctp_init_chunk *init_cp, init_buf; @@ -1110,7 +1125,8 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, ph = mtod(op_err, struct sctp_paramhdr *); ph->param_type = htons(SCTP_CAUSE_COOKIE_IN_SHUTDOWN); ph->param_length = htons(sizeof(struct sctp_paramhdr)); - sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag); + sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag, + vrf_id, table_id); if (how_indx < sizeof(asoc->cookie_how)) asoc->cookie_how[how_indx] = 2; return (NULL); @@ -1168,7 +1184,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, case SCTP_STATE_COOKIE_WAIT: case SCTP_STATE_COOKIE_ECHOED: /* - * INIT was sent, but got got a COOKIE_ECHO with the + * INIT was sent but got a COOKIE_ECHO with the * correct tags... just accept it...but we must * process the init so that we can make sure we have * the right seq no's. @@ -1233,7 +1249,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, * we're in the OPEN state (or beyond), so peer must * have simply lost the COOKIE-ACK */ - break; } /* end switch */ sctp_stop_all_cookie_timers(stcb); @@ -1255,7 +1270,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, if (how_indx < sizeof(asoc->cookie_how)) asoc->cookie_how[how_indx] = 5; return (stcb); - } /* end if */ + } if (ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag && ntohl(init_cp->init.initiate_tag) == asoc->peer_vtag && cookie->tie_tag_my_vtag == 0 && @@ -1543,7 +1558,8 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len, struct sctp_inpcb *inp, struct sctp_nets **netp, struct sockaddr *init_src, int *notification, - int auth_skipped, uint32_t auth_offset, uint32_t auth_len) + int auth_skipped, uint32_t auth_offset, uint32_t auth_len, + uint32_t vrf_id, uint32_t table_id) { struct sctp_tcb *stcb; struct sctp_init_chunk *init_cp, init_buf; @@ -1553,7 +1569,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, struct sockaddr_in *sin; struct sockaddr_in6 *sin6; struct sctp_association *asoc; - uint32_t vrf_id; int chk_length; int init_offset, initack_offset, initack_limit; int retval; @@ -1561,8 +1576,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, uint32_t old_tag; uint8_t auth_chunk_buf[SCTP_PARAM_BUFFER_SIZE]; - vrf_id = inp->def_vrf_id; - /* * find and validate the INIT chunk in the cookie (peer's info) the * INIT should start after the cookie-echo header struct (chunk @@ -1633,15 +1646,17 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, if (sctp_debug_on & SCTP_DEBUG_INPUT1) { printf("process_cookie_new: no room for another TCB!\n"); } -#endif /* SCTP_DEBUG */ +#endif op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen, - sh, op_err); + sh, op_err, vrf_id, table_id); return (NULL); } /* get the correct sctp_nets */ *netp = sctp_findnet(stcb, init_src); asoc = &stcb->asoc; + /* save the table id (vrf_id is done in aloc_assoc) */ + asoc->table_id = table_id; /* get scope variables out of cookie */ asoc->ipv4_local_scope = cookie->ipv4_scope; asoc->site_scope = cookie->site_scope; @@ -1659,7 +1674,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen, - sh, op_err); + sh, op_err, vrf_id, table_id); return (NULL); } /* process the INIT-ACK info (my info) */ @@ -1817,7 +1832,8 @@ static struct mbuf * sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, struct sctp_cookie_echo_chunk *cp, struct sctp_inpcb **inp_p, struct sctp_tcb **stcb, struct sctp_nets **netp, - int auth_skipped, uint32_t auth_offset, uint32_t auth_len, struct sctp_tcb **locked_tcb) + int auth_skipped, uint32_t auth_offset, uint32_t auth_len, + struct sctp_tcb **locked_tcb, uint32_t vrf_id, uint32_t table_id) { struct sctp_state_cookie *cookie; struct sockaddr_in6 sin6; @@ -1892,7 +1908,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, * invalid ports or bad tag. Note that we always leave the * v_tag in the header in network order and when we stored * it in the my_vtag slot we also left it in network order. - * This maintians the match even though it may be in the + * This maintains the match even though it may be in the * opposite byte order of the machine :-> */ return (NULL); @@ -2029,7 +2045,8 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, if (tim == 0) tim = now.tv_usec - cookie->time_entered.tv_usec; scm->time_usec = htonl(tim); - sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag); + sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag, + vrf_id, table_id); return (NULL); } /* @@ -2103,13 +2120,13 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, /* this is the "normal" case... get a new TCB */ *stcb = sctp_process_cookie_new(m, iphlen, offset, sh, cookie, cookie_len, *inp_p, netp, to, ¬ification, - auth_skipped, auth_offset, auth_len); + auth_skipped, auth_offset, auth_len, vrf_id, table_id); } else { /* this is abnormal... cookie-echo on existing TCB */ had_a_existing_tcb = 1; *stcb = sctp_process_cookie_existing(m, iphlen, offset, sh, cookie, cookie_len, *inp_p, *stcb, *netp, to, ¬ification, - &sac_restart_id); + &sac_restart_id, vrf_id, table_id); } if (*stcb == NULL) { @@ -2188,7 +2205,8 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, #endif /* SCTP_DEBUG */ op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); sctp_abort_association(*inp_p, NULL, m, iphlen, - sh, op_err); + sh, op_err, vrf_id, + table_id); sctp_free_assoc(*inp_p, *stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_20); return (NULL); } @@ -3527,7 +3545,8 @@ sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp, static struct sctp_tcb * sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb *inp, - struct sctp_tcb *stcb, struct sctp_nets **netp, int *fwd_tsn_seen) + struct sctp_tcb *stcb, struct sctp_nets **netp, int *fwd_tsn_seen, + uint32_t vrf_id, uint32_t table_id) { struct sctp_association *asoc; uint32_t vtag_in; @@ -3639,7 +3658,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, } if (stcb == NULL) { /* no association, so it's out of the blue... */ - sctp_handle_ootb(m, iphlen, *offset, sh, inp, NULL); + sctp_handle_ootb(m, iphlen, *offset, sh, inp, NULL, + vrf_id, table_id); *offset = length; if (locked_tcb) SCTP_TCB_UNLOCK(locked_tcb); @@ -3673,7 +3693,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, if (locked_tcb) SCTP_TCB_UNLOCK(locked_tcb); sctp_handle_ootb(m, iphlen, *offset, sh, inp, - NULL); + NULL, vrf_id, table_id); return (NULL); } } else { @@ -3705,7 +3725,6 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, *netp); } process_control_chunks: - while (IS_SCTP_CONTROL(ch)) { /* validate chunk length */ chk_length = ntohs(ch->chunk_length); @@ -3761,7 +3780,6 @@ process_control_chunks: /* We can fit it all */ goto all_fits; } - } else { /* get a complete chunk... */ if ((size_t)chk_length > sizeof(chunk_buf)) { @@ -3865,7 +3883,7 @@ process_control_chunks: return (NULL); } sctp_handle_init(m, iphlen, *offset, sh, - (struct sctp_init_chunk *)ch, inp, stcb, *netp, &abort_no_unlock); + (struct sctp_init_chunk *)ch, inp, stcb, *netp, &abort_no_unlock, vrf_id, table_id); if (abort_no_unlock) return (NULL); @@ -3903,7 +3921,7 @@ process_control_chunks: return (NULL); } ret = sctp_handle_init_ack(m, iphlen, *offset, sh, - (struct sctp_init_ack_chunk *)ch, stcb, *netp, &abort_no_unlock); + (struct sctp_init_ack_chunk *)ch, stcb, *netp, &abort_no_unlock, vrf_id, table_id); /* * Special case, I must call the output routine to * get the cookie echoed @@ -3938,9 +3956,10 @@ process_control_chunks: printf("Bad size on sack chunk .. to small\n"); } #endif - if (locked_tcb) - SCTP_TCB_UNLOCK(locked_tcb); *offset = length; + if (locked_tcb) + SCTP_TCB_UNLOCK(locked_tcb); + return (NULL); } sack = (struct sctp_sack_chunk *)ch; @@ -3972,8 +3991,6 @@ process_control_chunks: } if (abort_now) { /* ABORT signal from sack processing */ - if (locked_tcb) - SCTP_TCB_UNLOCK(locked_tcb); *offset = length; return (NULL); } @@ -4000,9 +4017,9 @@ process_control_chunks: #endif /* SCTP_DEBUG */ if (chk_length != sizeof(struct sctp_heartbeat_chunk)) { /* Its not ours */ + *offset = length; if (locked_tcb) SCTP_TCB_UNLOCK(locked_tcb); - *offset = length; return (NULL); } /* He's alive so give him credit */ @@ -4099,8 +4116,9 @@ process_control_chunks: */ goto process_cookie_anyway; } - sctp_abort_association(inp, stcb, m, iphlen, sh, - NULL); + sctp_abort_association(inp, stcb, m, iphlen, + sh, NULL, vrf_id, + table_id); *offset = length; return (NULL); } else if (inp->sctp_socket->so_qlimit) { @@ -4126,7 +4144,7 @@ process_control_chunks: htons(sizeof(struct sctp_paramhdr)); } sctp_abort_association(inp, stcb, m, - iphlen, sh, oper); + iphlen, sh, oper, vrf_id, table_id); } *offset = length; return (NULL); @@ -4152,7 +4170,9 @@ process_control_chunks: auth_skipped, auth_offset, auth_len, - &locked_tcb); + &locked_tcb, + vrf_id, + table_id); if (linp) SCTP_ASOC_CREATE_UNLOCK(linp); if (ret_buf == NULL) { @@ -4195,7 +4215,11 @@ process_control_chunks: printf("SCTP_COOKIE-ACK\n"); } #endif /* SCTP_DEBUG */ - + if (chk_length != sizeof(struct sctp_cookie_ack_chunk)) { + if (locked_tcb) + SCTP_TCB_UNLOCK(locked_tcb); + return (NULL); + } if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { /* We are not interested anymore */ if ((stcb) && (stcb->asoc.total_output_queue_size)) { @@ -4564,7 +4588,7 @@ int sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int length, struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net, - uint8_t ecn_bits) + uint8_t ecn_bits, uint32_t vrf_id, uint32_t table_id) { /* * Control chunk processing @@ -4596,7 +4620,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, if (IS_SCTP_CONTROL(ch)) { /* process the control portion of the SCTP packet */ stcb = sctp_process_control(m, iphlen, &offset, length, sh, ch, - inp, stcb, &net, &fwd_tsn_seen); + inp, stcb, &net, &fwd_tsn_seen, vrf_id, table_id); if (stcb) { /* * This covers us if the cookie-echo was there and @@ -4625,7 +4649,8 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, } if (stcb == NULL) { /* out of the blue DATA chunk */ - sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL); + sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL, + vrf_id, table_id); return (1); } if (stcb->asoc.my_vtag != ntohl(sh->v_tag)) { @@ -4687,7 +4712,8 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, /* * We consider OOTB any data sent during asoc setup. */ - sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL); + sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL, + vrf_id, table_id); SCTP_TCB_UNLOCK(stcb); return (1); break; @@ -4800,7 +4826,7 @@ sctp_input(i_pak, off) #endif struct mbuf *m; int iphlen; - uint32_t vrf_id; + uint32_t vrf_id = 0, table_id = 0; uint8_t ecn_bits; struct ip *ip; struct sctphdr *sh; @@ -4813,7 +4839,15 @@ sctp_input(i_pak, off) int refcount_up = 0; int length, mlen, offset; - vrf_id = SCTP_DEFAULT_VRFID; + + if (SCTP_GET_PKT_VRFID(i_pak, vrf_id)) { + SCTP_RELEASE_PKT(i_pak); + return; + } + if (SCTP_GET_PKT_TABLEID(i_pak, table_id)) { + SCTP_RELEASE_PKT(i_pak); + return; + } mlen = SCTP_HEADER_LEN(i_pak); iphlen = off; m = SCTP_HEADER_TO_CHAIN(i_pak); @@ -4948,14 +4982,16 @@ sctp_skip_csum_4: sh->v_tag = init_chk->init.initiate_tag; } if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { - sctp_send_shutdown_complete2(m, iphlen, sh); + sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, + table_id); goto bad; } if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) { goto bad; } if (ch->chunk_type != SCTP_ABORT_ASSOCIATION) - sctp_send_abort(m, iphlen, sh, 0, NULL); + sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, + table_id); goto bad; } else if (stcb == NULL) { refcount_up = 1; @@ -4982,7 +5018,8 @@ sctp_skip_csum_4: ecn_bits = ip->ip_tos; sctp_common_input_processing(&m, iphlen, offset, length, sh, ch, - inp, stcb, net, ecn_bits); + inp, stcb, net, ecn_bits, vrf_id, + table_id); /* inp's ref-count reduced && stcb unlocked */ if (m) { sctp_m_freem(m); @@ -5008,6 +5045,7 @@ bad: sctp_m_freem(m); } /* For BSD/MAC this does nothing */ - SCTP_RELEASE_PAK(i_pak); + SCTP_DETACH_HEADER_FROM_CHAIN(i_pak); + SCTP_RELEASE_HEADER(i_pak); return; } diff --git a/sys/netinet/sctp_input.h b/sys/netinet/sctp_input.h index c34b8cc..047cab4 100644 --- a/sys/netinet/sctp_input.h +++ b/sys/netinet/sctp_input.h @@ -40,14 +40,16 @@ __FBSDID("$FreeBSD$"); int sctp_common_input_processing(struct mbuf **, int, int, int, struct sctphdr *, struct sctp_chunkhdr *, struct sctp_inpcb *, - struct sctp_tcb *, struct sctp_nets *, uint8_t); + struct sctp_tcb *, struct sctp_nets *, uint8_t, uint32_t, uint32_t); struct sctp_stream_reset_out_request * - sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq, struct sctp_tmit_chunk **bchk); +sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq, + struct sctp_tmit_chunk **bchk); -void - sctp_reset_in_stream(struct sctp_tcb *stcb, int number_entries, uint16_t * list); +void +sctp_reset_in_stream(struct sctp_tcb *stcb, int number_entries, + uint16_t * list); #endif diff --git a/sys/netinet/sctp_os_bsd.h b/sys/netinet/sctp_os_bsd.h index b01287e..a2d4bf3 100644 --- a/sys/netinet/sctp_os_bsd.h +++ b/sys/netinet/sctp_os_bsd.h @@ -107,15 +107,41 @@ __FBSDID("$FreeBSD$"); #define USER_ADDR_NULL (NULL) /* FIX ME: temp */ #define SCTP_LIST_EMPTY(list) LIST_EMPTY(list) +#if defined(SCTP_DEBUG) +#define SCTPDBG(level, params...) \ +{ \ + do { \ + if (sctp_debug_on & level ) { \ + printf(params); \ + } \ + } while (0); \ +} +#define SCTPDBG_ADDR(level, addr) \ +{ \ + do { \ + if (sctp_debug_on & level ) { \ + sctp_print_address(addr); \ + } \ + } while (0); \ +} +#define SCTP_PRINTF(params...) printf(params) +#else +#define SCTPDBG(level, params...) +#define SCTPDBG_ADDR(level, addr) +#define SCTP_PRINTF(params...) +#endif + /* * Local address and interface list handling */ -#define SCTP_MAX_VRF_ID 0 -#define SCTP_SIZE_OF_VRF_HASH 3 -#define SCTP_IFNAMSIZ IFNAMSIZ -#define SCTP_DEFAULT_VRFID 0 -#define SCTP_VRF_HASH_SIZE 16 - +#define SCTP_MAX_VRF_ID 0 +#define SCTP_SIZE_OF_VRF_HASH 3 +#define SCTP_IFNAMSIZ IFNAMSIZ +#define SCTP_DEFAULT_VRFID 0 +#define SCTP_DEFAULT_TABLEID 0 +#define SCTP_VRF_ADDR_HASH_SIZE 16 +#define SCTP_VRF_IFN_HASH_SIZE 3 +#define SCTP_VRF_DEFAULT_TABLEID(vrf_id) 0 #define SCTP_IFN_IS_IFT_LOOP(ifn) ((ifn)->ifn_type == IFT_LOOP) @@ -124,7 +150,8 @@ __FBSDID("$FreeBSD$"); */ /* This could return VOID if the index works but for BSD we provide both. */ #define SCTP_GET_IFN_VOID_FROM_ROUTE(ro) (void *)ro->ro_rt->rt_ifp -#define SCTP_GET_IF_INDEX_FROM_ROUTE(ro) ro->ro_rt->rt_ifp->if_index +#define SCTP_GET_IF_INDEX_FROM_ROUTE(ro) (ro)->ro_rt->rt_ifp->if_index +#define SCTP_ROUTE_HAS_VALID_IFN(ro) ((ro)->ro_rt && (ro)->ro_rt->rt_ifp) /* * general memory allocation @@ -205,6 +232,20 @@ typedef struct callout sctp_os_timer_t; } else if ((m->m_flags & M_EXT) == 0) { \ M_ALIGN(m, len); \ } +/*************************/ +/* MTU */ +/*************************/ +#define SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index) ((struct ifnet *)ifn)->if_mtu +#define SCTP_GATHER_MTU_FROM_ROUTE(sctp_ifa, sa, rt) ((rt != NULL) ? rt->rt_rmx.rmx_mtu : 0) +#define SCTP_GATHER_MTU_FROM_INTFC(sctp_ifn) ((sctp_ifn->ifn_p != NULL) ? ((struct ifnet *)(sctp_ifn->ifn_p))->if_mtu : 0) +#define SCTP_SET_MTU_OF_ROUTE(sa, rt, mtu) do { \ + if (rt != NULL) \ + rt->rt_rmx.rmx_mtu = mtu; \ + } while(0) + +/* (de-)register interface event notifications */ +#define SCTP_REGISTER_INTERFACE(ifhandle, ifname) +#define SCTP_DEREGISTER_INTERFACE(ifhandle, ifname) /*************************/ /* These are for logging */ @@ -224,13 +265,28 @@ typedef struct callout sctp_os_timer_t; * chain pointers.. thus the macro. */ #define SCTP_HEADER_TO_CHAIN(m) (m) +#define SCTP_DETACH_HEADER_FROM_CHAIN(m) #define SCTP_HEADER_LEN(m) (m->m_pkthdr.len) -#define SCTP_GET_HEADER_FOR_OUTPUT(len) sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA) -#define SCTP_RELEASE_PAK(i_pak) +#define SCTP_GET_HEADER_FOR_OUTPUT(o_pak) 0 +#define SCTP_RELEASE_HEADER(m) +#define SCTP_RELEASE_PKT(m) sctp_m_freem(m) + +static inline int +SCTP_GET_PKT_VRFID(void *m, uint32_t vrf_id) +{ + vrf_id = SCTP_DEFAULT_VRFID; + return (0); +} +static inline int +SCTP_GET_PKT_TABLEID(void *m, uint32_t table_id) +{ + table_id = SCTP_DEFAULT_TABLEID; + return (0); +} /* Attach the chain of data into the sendable packet. */ #define SCTP_ATTACH_CHAIN(pak, m, packet_length) do { \ - pak->m_next = m; \ + pak = m; \ pak->m_pkthdr.len = packet_length; \ } while(0) @@ -248,6 +304,9 @@ typedef struct callout sctp_os_timer_t; #define SCTP_GET_IPV4_LENGTH(iph) (iph->ip_len) #define SCTP_GET_IPV6_LENGTH(ip6) (ntohs(ip6->ip6_plen)) +/* get the v6 hop limit */ +#define SCTP_GET_HLIM(inp, ro) in6_selecthlim((struct in6pcb *)&inp->ip_inp.inp, (ro ? (ro->ro_rt ? (ro->ro_rt->rt_ifp) : (NULL)) : (NULL))); + /* is the endpoint v6only? */ #define SCTP_IPV6_V6ONLY(inp) (((struct inpcb *)inp)->inp_flags & IN6P_IPV6_V6ONLY) /* is the socket non-blocking? */ @@ -258,6 +317,8 @@ typedef struct callout sctp_os_timer_t; #define SCTP_SO_TYPE(so) ((so)->so_type) /* reserve sb space for a socket */ #define SCTP_SORESERVE(so, send, recv) soreserve(so, send, recv) +/* wakeup a socket */ +#define SCTP_SOWAKEUP(so) wakeup(&(so)->so_timeo) /* clear the socket buffer state */ #define SCTP_SB_CLEAR(sb) \ (sb).sb_cc = 0; \ @@ -271,7 +332,36 @@ typedef struct callout sctp_os_timer_t; * routes, output, etc. */ typedef struct route sctp_route_t; +typedef struct rtentry sctp_rtentry_t; + +#define SCTP_RTALLOC(ro, vrf_id, table_id) rtalloc_ign((struct route *)ro, 0UL) +/* Future zero copy wakeup/send function */ +#define SCTP_ZERO_COPY_EVENT(inp, so) + +/* + * IP output routines + */ +#define SCTP_IP_OUTPUT(result, o_pak, ro, stcb, vrf_id, table_id) \ +{ \ + int o_flgs = 0; \ + if (stcb && stcb->sctp_ep && stcb->sctp_ep->sctp_socket) { \ + o_flgs = IP_RAWOUTPUT | (stcb->sctp_ep->sctp_socket->so_options & SO_DONTROUTE); \ + } else { \ + o_flgs = IP_RAWOUTPUT; \ + } \ + result = ip_output(o_pak, NULL, ro, o_flgs, 0, NULL); \ +} + +#define SCTP_IP6_OUTPUT(result, o_pak, ro, ifp, stcb, vrf_id, table_id) \ +{ \ + if (stcb && stcb->sctp_ep) \ + result = ip6_output(o_pak, \ + ((struct in6pcb *)(stcb->sctp_ep))->in6p_outputopts, \ + (ro), 0, 0, ifp, NULL); \ + else \ + result = ip6_output(o_pak, NULL, (ro), 0, 0, ifp, NULL); \ +} struct mbuf * sctp_get_mbuf_for_msg(unsigned int space_needed, diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 96d9495..b13e9c4 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -2880,7 +2880,9 @@ bound_all_plan_b: printf("Plan C no preferred for Dest, acceptable for?\n"); } #endif - + if (emit_ifn == NULL) { + goto plan_d; + } LIST_FOREACH(sctp_ifa, &emit_ifn->ifalist, next_ifa) { if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) @@ -2902,7 +2904,7 @@ bound_all_plan_b: atomic_add_int(&sifa->refcount, 1); return (sifa); } - +plan_d: /* * plan_d: We are in trouble. No preferred address on the emit * interface. And not even a perfered address on all interfaces. Go @@ -2969,7 +2971,6 @@ sctp_source_address_selection(struct sctp_inpcb *inp, struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ro->ro_dst; struct sctp_ifa *answer; uint8_t dest_is_priv, dest_is_loop; - int did_rtalloc = 0; sa_family_t fam; /* @@ -3024,11 +3025,17 @@ sctp_source_address_selection(struct sctp_inpcb *inp, * we must use rotation amongst the bound addresses.. */ if (ro->ro_rt == NULL) { + uint32_t table_id = 0; + /* * Need a route to cache. */ - rtalloc_ign(ro, 0UL); - did_rtalloc = 1; + if (stcb) { + table_id = stcb->asoc.table_id; + } else { + table_id = SCTP_VRF_DEFAULT_TABLEID(vrf_id); + } + SCTP_RTALLOC(ro, vrf_id, table_id); } if (ro->ro_rt == NULL) { return (NULL); @@ -3327,25 +3334,22 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, */ /* Will need ifdefs around this */ struct mbuf *o_pak; - + struct mbuf *newm; struct sctphdr *sctphdr; int packet_length; - int o_flgs; uint32_t csum; int ret; - unsigned int have_mtu; uint32_t vrf_id; - sctp_route_t *ro; - + sctp_route_t *ro = NULL; if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) { sctp_m_freem(m); return (EFAULT); } - if (stcb == NULL) { - vrf_id = inp->def_vrf_id; - } else { + if (stcb) { vrf_id = stcb->asoc.vrf_id; + } else { + vrf_id = inp->def_vrf_id; } /* fill in the HMAC digest for any AUTH chunk in the packet */ @@ -3354,7 +3358,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } /* Calculate the csum and fill in the length of the packet */ sctphdr = mtod(m, struct sctphdr *); - have_mtu = 0; if (sctp_no_csum_on_loopback && (stcb) && (stcb->asoc.loopback_scope)) { @@ -3376,17 +3379,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, sctp_route_t iproute; uint8_t tos_value; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip)); - if (o_pak == NULL) { - /* failed to prepend data, give up */ + newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA); + if (newm == NULL) { sctp_m_freem(m); return (ENOMEM); } - SCTP_ALIGN_TO_END(o_pak, sizeof(struct ip)); - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip); + SCTP_ALIGN_TO_END(newm, sizeof(struct ip)); + SCTP_BUF_LEN(newm) = sizeof(struct ip); packet_length += sizeof(struct ip); - SCTP_ATTACH_CHAIN(o_pak, m, packet_length); - ip = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip *); + SCTP_BUF_NEXT(newm) = m; + m = newm; + ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) >> 2); if (net) { @@ -3403,12 +3406,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else ip->ip_off = 0; - /* FreeBSD has a function for ip_id's */ ip->ip_id = ip_newid(); ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl; - ip->ip_len = SCTP_HEADER_LEN(o_pak); + ip->ip_len = packet_length; if (stcb) { if ((stcb->asoc.ecn_allowed) && ecn_ok) { /* Enable ECN */ @@ -3435,10 +3437,16 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* call the routine to select the src address */ if (net) { + if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + } if (net->src_addr_selected == 0) { /* Cache the source address */ net->ro._s_addr = sctp_source_address_selection(inp, stcb, - ro, net, out_of_asoc_ok, vrf_id); + ro, net, out_of_asoc_ok, + vrf_id); if (net->ro._s_addr == NULL) { /* No route to host */ goto no_route; @@ -3514,15 +3522,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } } } - sctp_m_freem(o_pak); return (EHOSTUNREACH); - } else { - have_mtu = ro->ro_rt->rt_ifp->if_mtu; } - if (inp->sctp_socket) { - o_flgs = (IP_RAWOUTPUT | (inp->sctp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); - } else { - o_flgs = IP_RAWOUTPUT; + if (ro != &iproute) { + memcpy(&iproute, ro, sizeof(*ro)); } #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { @@ -3532,23 +3535,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, printf("RTP route is %p through\n", ro->ro_rt); } #endif -#ifdef _WHY_THIS_CODE - if ((have_mtu) && (net) && (have_mtu > net->mtu)) { - ro->ro_rt->rt_ifp->if_mtu = net->mtu; - } -#endif - if (ro != &iproute) { - memcpy(&iproute, ro, sizeof(*ro)); - } - ret = ip_output(o_pak, inp->ip_inp.inp.inp_options, - ro, o_flgs, inp->ip_inp.inp.inp_moptions - ,(struct inpcb *)NULL - ); -#ifdef _WHY_THIS_CODE - if ((ro->ro_rt) && (have_mtu) && (net) && (have_mtu > net->mtu)) { - ro->ro_rt->rt_ifp->if_mtu = have_mtu; + + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* failed to prepend data, give up */ + sctp_m_freem(m); + return (ENOMEM); } -#endif + SCTP_ATTACH_CHAIN(o_pak, m, packet_length); + + /* send it out. table id is taken from stcb */ + SCTP_IP_OUTPUT(ret, o_pak, ro, stcb, vrf_id, 0); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); if (ret) @@ -3568,10 +3565,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else { /* PMTU check versus smallest asoc MTU goes here */ if (ro->ro_rt != NULL) { - if (ro->ro_rt->rt_rmx.rmx_mtu && - (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) { - sctp_mtu_size_reset(inp, &stcb->asoc, - ro->ro_rt->rt_rmx.rmx_mtu); + uint32_t mtu; + + mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); + if (mtu && + (stcb->asoc.smallest_mtu > mtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called after ip_output mtu-change:%d\n", + mtu); +#endif + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + net->mtu = mtu; } } else { /* route was freed */ @@ -3589,15 +3593,14 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, else if (to->sa_family == AF_INET6) { uint32_t flowlabel; struct ip6_hdr *ip6h; - struct route_in6 ip6route; struct ifnet *ifp; u_char flowTop; uint16_t flowBottom; u_char tosBottom, tosTop; struct sockaddr_in6 *sin6, tmp, *lsa6, lsa6_tmp; - struct sockaddr_in6 lsa6_storage; int prev_scope = 0; + struct sockaddr_in6 lsa6_storage; int error; u_short prev_port = 0; @@ -3606,18 +3609,19 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else { flowlabel = ((struct in6pcb *)inp)->in6p_flowinfo; } - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip6_hdr)); - if (o_pak == NULL) { - /* failed to prepend data, give up */ + + newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA); + if (newm == NULL) { sctp_m_freem(m); return (ENOMEM); } - SCTP_ALIGN_TO_END(o_pak, sizeof(struct ip6_hdr)); - - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip6_hdr); + SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr)); + SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr); packet_length += sizeof(struct ip6_hdr); - SCTP_ATTACH_CHAIN(o_pak, m, packet_length); - ip6h = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip6_hdr *); + SCTP_BUF_NEXT(newm) = m; + m = newm; + + ip6h = mtod(m, struct ip6_hdr *); /* * We assume here that inp_flow is in host byte order within * the TCB! @@ -3654,7 +3658,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom)); ip6h->ip6_nxt = IPPROTO_SCTP; - ip6h->ip6_plen = (SCTP_HEADER_LEN(o_pak) - sizeof(struct ip6_hdr)); + ip6h->ip6_plen = (packet_length - sizeof(struct ip6_hdr)); ip6h->ip6_dst = sin6->sin6_addr; /* @@ -3667,6 +3671,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, lsa6_tmp.sin6_len = sizeof(lsa6_tmp); lsa6 = &lsa6_tmp; if (net) { + if (net->ro._s_addr && net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + } if (net->src_addr_selected == 0) { /* Cache the source address */ net->ro._s_addr = sctp_source_address_selection(inp, @@ -3712,7 +3721,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, lsa6_storage.sin6_family = AF_INET6; lsa6_storage.sin6_len = sizeof(lsa6_storage); if ((error = sa6_recoverscope(&lsa6_storage)) != 0) { - sctp_m_freem(o_pak); + sctp_m_freem(m); return (error); } /* XXX */ @@ -3725,12 +3734,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * We set the hop limit now since there is a good chance * that our ro pointer is now filled */ - ip6h->ip6_hlim = in6_selecthlim((struct in6pcb *)&inp->ip_inp.inp, - (ro ? - (ro->ro_rt ? (ro->ro_rt->rt_ifp) : (NULL)) : - (NULL))); - o_flgs = 0; - ifp = ro->ro_rt->rt_ifp; + ip6h->ip6_hlim = SCTP_GET_HLIM(inp, ro); + ifp = SCTP_GET_IFN_VOID_FROM_ROUTE(ro); + #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { /* Copy to be sure something bad is not happening */ @@ -3743,20 +3749,24 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, printf("dst: "); sctp_print_address((struct sockaddr *)sin6); } -#endif /* SCTP_DEBUG */ +#endif if (net) { sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; /* preserve the port and scope for link local send */ prev_scope = sin6->sin6_scope_id; prev_port = sin6->sin6_port; } - ret = ip6_output(o_pak, ((struct in6pcb *)inp)->in6p_outputopts, - (struct route_in6 *)ro, - o_flgs, - ((struct in6pcb *)inp)->in6p_moptions, - &ifp - ,NULL - ); + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* failed to prepend data, give up */ + sctp_m_freem(m); + return (ENOMEM); + } + SCTP_ATTACH_CHAIN(o_pak, m, packet_length); + + /* send it out. table id is taken from stcb */ + SCTP_IP6_OUTPUT(ret, o_pak, (struct route_in6 *)ro, &ifp, + stcb, vrf_id, 0); + if (net) { /* for link local this must be done */ sin6->sin6_scope_id = prev_scope; @@ -3766,7 +3776,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { printf("return from send is %d\n", ret); } -#endif /* SCTP_DEBUG_OUTPUT */ +#endif SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); if (ret) { @@ -3781,7 +3791,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* PMTU check versus smallest asoc MTU goes here */ if (ro->ro_rt == NULL) { /* Route was freed */ - if (net->ro._s_addr && net->src_addr_selected) { sctp_free_ifa(net->ro._s_addr); @@ -3790,15 +3799,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, net->src_addr_selected = 0; } if (ro->ro_rt != NULL) { - if (ro->ro_rt->rt_rmx.rmx_mtu && - (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) { - sctp_mtu_size_reset(inp, - &stcb->asoc, - ro->ro_rt->rt_rmx.rmx_mtu); + uint32_t mtu; + + mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); + if (mtu && + (stcb->asoc.smallest_mtu > mtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called after ip6_output mtu-change:%d\n", + mtu); +#endif + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + net->mtu = mtu; } } else if (ifp) { if (ND_IFINFO(ifp)->linkmtu && (stcb->asoc.smallest_mtu > ND_IFINFO(ifp)->linkmtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called via ifp ND_IFINFO() linkmtu:%d\n", + ND_IFINFO(ifp)->linkmtu); +#endif sctp_mtu_size_reset(inp, &stcb->asoc, ND_IFINFO(ifp)->linkmtu); @@ -4544,7 +4563,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc, void sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct mbuf *init_pkt, int iphlen, int offset, struct sctphdr *sh, - struct sctp_init_chunk *init_chk) + struct sctp_init_chunk *init_chk, uint32_t vrf_id, uint32_t table_id) { struct sctp_association *asoc; struct mbuf *m, *m_at, *m_tmp, *m_cookie, *op_err, *mp_last; @@ -4567,15 +4586,11 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int abort_flag, padval, sz_of; int num_ext; int p_len; - uint32_t vrf_id; - if (stcb) { + if (stcb) asoc = &stcb->asoc; - vrf_id = asoc->vrf_id; - } else { - vrf_id = inp->def_vrf_id; + else asoc = NULL; - } mp_last = NULL; if ((asoc != NULL) && (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) && @@ -4586,7 +4601,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * though we even set the T bit and copy in the 0 tag.. this * looks no different than if no listener was present. */ - sctp_send_abort(init_pkt, iphlen, sh, 0, NULL); + sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id, + table_id); return; } abort_flag = 0; @@ -4594,7 +4610,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, (offset + sizeof(struct sctp_init_chunk)), &abort_flag, (struct sctp_chunkhdr *)init_chk); if (abort_flag) { - sctp_send_abort(init_pkt, iphlen, sh, init_chk->init.initiate_tag, op_err); + sctp_send_abort(init_pkt, iphlen, sh, + init_chk->init.initiate_tag, op_err, vrf_id, + table_id); return; } m = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); @@ -4682,7 +4700,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, ro = &iproute; memcpy(&ro->ro_dst, sin, sizeof(*sin)); addr = sctp_source_address_selection(inp, NULL, - ro, NULL, 0, vrf_id); + ro, NULL, 0, + vrf_id); if (addr == NULL) return; @@ -4711,9 +4730,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, stc.site_scope = 1; stc.local_scope = 0; } + sctp_free_ifa(addr); } else if (iph->ip_v == (IPV6_VERSION >> 4)) { struct sctp_ifa *addr; - struct route_in6 iproute6; ip6 = mtod(init_pkt, struct ip6_hdr *); @@ -4780,6 +4799,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, } memcpy(&stc.laddress, &addr->address.sin6.sin6_addr, sizeof(struct in6_addr)); stc.laddr_type = SCTP_IPV6_ADDRESS; + sctp_free_ifa(addr); } } else { /* set the scope per the existing tcb */ @@ -5446,6 +5466,7 @@ sctp_msg_append(struct sctp_tcb *stcb, SCTP_GETTIME_TIMEVAL(&sp->ts); sp->stream = srcv->sinfo_stream; sp->msg_is_complete = 1; + sp->sender_all_done = 1; sp->some_taken = 0; sp->data = m; sp->tail_mbuf = NULL; @@ -5509,7 +5530,9 @@ error_out: if (can_take_mbuf) { appendchain = clonechain; } else { - if (!copy_by_ref && (sizeofcpy <= ((((sctp_mbuf_threshold_count - 1) * MLEN) + MHLEN)))) { + if (!copy_by_ref && + (sizeofcpy <= ((((sctp_mbuf_threshold_count - 1) * MLEN) + MHLEN))) + ) { /* Its not in a cluster */ if (*endofchain == NULL) { /* lets get a mbuf cluster */ @@ -8025,7 +8048,8 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, #ifdef SCTP_AUDITING_ENABLED sctp_audit_log(0xC3, 1); #endif - if (TAILQ_EMPTY(&asoc->sent_queue)) { + if ((TAILQ_EMPTY(&asoc->sent_queue)) && + (TAILQ_EMPTY(&asoc->control_send_queue))) { #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) { printf("SCTP hits empty queue with cnt set to %d?\n", @@ -8780,7 +8804,6 @@ sctp_output(inp, m, addr, control, p, flags) struct mbuf *m; struct sockaddr *addr; struct mbuf *control; - struct thread *p; int flags; { @@ -9338,25 +9361,26 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb, } int -sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) +sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh, + uint32_t vrf_id, uint32_t table_id) { /* formulate and SEND a SHUTDOWN-COMPLETE */ struct mbuf *o_pak; struct mbuf *mout; struct ip *iph, *iph_out; struct ip6_hdr *ip6, *ip6_out; - int offset_out, len; + int offset_out, len, mlen; struct sctp_shutdown_complete_msg *comp_cp; /* Get room for the largest message */ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg)); - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(len); - if (o_pak == NULL) { - /* no mbuf's */ + + mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA); + if (mout == NULL) { return (-1); } - mout = SCTP_HEADER_TO_CHAIN(o_pak); + SCTP_BUF_LEN(mout) = len; iph = mtod(m, struct ip *); iph_out = NULL; ip6_out = NULL; @@ -9408,8 +9432,11 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) /* Currently not supported. */ return (-1); } - - SCTP_HEADER_LEN(o_pak) = SCTP_BUF_LEN(mout); + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* no mbuf's */ + sctp_m_freem(mout); + return (-1); + } /* Now copy in and fill in the ABORT tags etc. */ comp_cp->sh.src_port = sh->dest_port; comp_cp->sh.dest_port = sh->src_port; @@ -9420,31 +9447,39 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) comp_cp->shut_cmp.ch.chunk_length = htons(sizeof(struct sctp_shutdown_complete_chunk)); /* add checksum */ - if ((sctp_no_csum_on_loopback) && SCTP_IS_IT_LOOPBACK(o_pak)) { + if ((sctp_no_csum_on_loopback) && SCTP_IS_IT_LOOPBACK(mout)) { comp_cp->sh.checksum = 0; } else { comp_cp->sh.checksum = sctp_calculate_sum(mout, NULL, offset_out); } if (iph_out != NULL) { sctp_route_t ro; + int ret; + struct sctp_tcb *stcb = NULL; + mlen = SCTP_BUF_LEN(mout); bzero(&ro, sizeof ro); /* set IPv4 length */ - iph_out->ip_len = SCTP_HEADER_LEN(o_pak); + iph_out->ip_len = mlen; + SCTP_ATTACH_CHAIN(o_pak, mout, mlen); + /* out it goes */ - ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + SCTP_IP_OUTPUT(ret, o_pak, &ro, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); } else if (ip6_out != NULL) { struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; bzero(&ro, sizeof(ro)); - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + mlen = SCTP_BUF_LEN(mout); + SCTP_ATTACH_CHAIN(o_pak, mout, mlen); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); @@ -9453,6 +9488,7 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) SCTP_STAT_INCR_COUNTER64(sctps_outpackets); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); return (0); + } static struct sctp_nets * @@ -10166,7 +10202,7 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, void sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, - struct mbuf *err_cause) + struct mbuf *err_cause, uint32_t vrf_id, uint32_t table_id) { /*- * Formulate the abort message, and send it back down. @@ -10176,7 +10212,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, struct sctp_abort_msg *abm; struct ip *iph, *iph_out; struct ip6_hdr *ip6, *ip6_out; - int iphlen_out; + int iphlen_out, len; /* don't respond to ABORT with ABORT */ if (sctp_is_there_an_abort_here(m, iphlen, &vtag)) { @@ -10184,13 +10220,14 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, sctp_m_freem(err_cause); return; } - o_pak = SCTP_GET_HEADER_FOR_OUTPUT((sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg))); - if (o_pak == NULL) { + len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg)); + + mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA); + if (mout == NULL) { if (err_cause) sctp_m_freem(err_cause); return; } - mout = SCTP_HEADER_TO_CHAIN(o_pak); iph = mtod(m, struct ip *); iph_out = NULL; ip6_out = NULL; @@ -10255,19 +10292,19 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, err_len += SCTP_BUF_LEN(m_tmp); m_tmp = SCTP_BUF_NEXT(m_tmp); } - SCTP_HEADER_LEN(o_pak) = SCTP_BUF_LEN(mout) + err_len; + len = SCTP_BUF_LEN(mout) + err_len; if (err_len % 4) { /* need pad at end of chunk */ uint32_t cpthis = 0; int padlen; - padlen = 4 - (SCTP_HEADER_LEN(o_pak) % 4); - m_copyback(mout, SCTP_HEADER_LEN(o_pak), padlen, (caddr_t)&cpthis); - SCTP_HEADER_LEN(o_pak) += padlen; + padlen = 4 - (len % 4); + m_copyback(mout, len, padlen, (caddr_t)&cpthis); + len += padlen; } abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch) + err_len); } else { - SCTP_HEADER_LEN(mout) = SCTP_BUF_LEN(mout); + len = SCTP_BUF_LEN(mout); abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch)); } @@ -10277,8 +10314,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } else { abm->sh.checksum = sctp_calculate_sum(mout, NULL, iphlen_out); } + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* no mbuf's */ + sctp_m_freem(mout); + return; + } if (iph_out != NULL) { sctp_route_t ro; + struct sctp_tcb *stcb = NULL; + int ret; /* zap the stack pointer to the route */ bzero(&ro, sizeof ro); @@ -10289,16 +10333,19 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } #endif /* set IPv4 length */ - iph_out->ip_len = SCTP_HEADER_LEN(o_pak); + iph_out->ip_len = len; /* out it goes */ - (void)ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP_OUTPUT(ret, o_pak, &ro, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); } else if (ip6_out != NULL) { struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; /* zap the stack pointer to the route */ bzero(&ro, sizeof(ro)); @@ -10308,10 +10355,10 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, sctp_print_address_pkt((struct ip *)ip6_out, &abm->sh); } #endif - ip6_out->ip6_plen = SCTP_HEADER_LEN(o_pak) - sizeof(*ip6_out); - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + ip6_out->ip6_plen = len - sizeof(*ip6_out); + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); @@ -10321,17 +10368,16 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } void -sctp_send_operr_to(struct mbuf *m, int iphlen, - struct mbuf *scm, - uint32_t vtag) +sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag, + uint32_t vrf_id, uint32_t table_id) { struct mbuf *o_pak; struct sctphdr *ihdr; int retcode; struct sctphdr *ohdr; struct sctp_chunkhdr *ophdr; - struct ip *iph; + struct mbuf *mout; #ifdef SCTP_DEBUG struct sockaddr_in6 lsa6, fsa6; @@ -10363,7 +10409,6 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, len += SCTP_BUF_LEN(at); at = SCTP_BUF_NEXT(at); } - ophdr->chunk_length = htons(len - sizeof(struct sctphdr)); if (len % 4) { /* need padding */ @@ -10379,22 +10424,28 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, } else { val = sctp_calculate_sum(scm, NULL, 0); } + mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA); + if (mout == NULL) { + sctp_m_freem(scm); + return; + } + SCTP_BUF_NEXT(mout) = scm; + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + sctp_m_freem(mout); + return; + } ohdr->checksum = val; if (iph->ip_v == IPVERSION) { /* V4 */ struct ip *out; sctp_route_t ro; + struct sctp_tcb *stcb = NULL; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip)); - if (o_pak == NULL) { - sctp_m_freem(scm); - return; - } - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip); + SCTP_BUF_LEN(mout) = sizeof(struct ip); len += sizeof(struct ip); - SCTP_ATTACH_CHAIN(o_pak, scm, len); + bzero(&ro, sizeof ro); - out = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip *); + out = mtod(mout, struct ip *); out->ip_v = iph->ip_v; out->ip_hl = (sizeof(struct ip) / 4); out->ip_tos = iph->ip_tos; @@ -10405,10 +10456,11 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, out->ip_sum = 0; out->ip_src = iph->ip_dst; out->ip_dst = iph->ip_src; - out->ip_len = SCTP_HEADER_LEN(o_pak); - retcode = ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + out->ip_len = len; + SCTP_ATTACH_CHAIN(o_pak, mout, len); + + SCTP_IP_OUTPUT(retcode, o_pak, &ro, stcb, vrf_id, table_id); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); /* Free the route if we got one back */ @@ -10417,20 +10469,16 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, } else { /* V6 */ struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; struct ip6_hdr *out6, *in6; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip6_hdr)); - if (o_pak == NULL) { - sctp_m_freem(scm); - return; - } - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip6_hdr); + SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr); len += sizeof(struct ip6_hdr); - SCTP_ATTACH_CHAIN(o_pak, scm, len); - bzero(&ro, sizeof ro); in6 = mtod(m, struct ip6_hdr *); - out6 = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip6_hdr *); + out6 = mtod(mout, struct ip6_hdr *); out6->ip6_flow = in6->ip6_flow; out6->ip6_hlim = ip6_defhlim; out6->ip6_nxt = IPPROTO_SCTP; @@ -10454,9 +10502,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, sctp_print_address((struct sockaddr *)&fsa6); } #endif /* SCTP_DEBUG */ - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); /* Free the route if we got one back */ @@ -10496,7 +10545,7 @@ sctp_copy_one(struct sctp_stream_queue_pending *sp, left = sp->length; sp->data = m_uiotombuf(uio, M_WAITOK, sp->length, - resv_upfront, M_PKTHDR); + resv_upfront, 0); if (sp->data == NULL) return (ENOMEM); @@ -10596,8 +10645,6 @@ out_now: } - - int sctp_sosend(struct socket *so, struct sockaddr *addr, @@ -10640,7 +10687,8 @@ sctp_lower_sosend(struct socket *so, struct mbuf *control, int flags, int use_rcvinfo, - struct sctp_sndrcvinfo *srcv, + struct sctp_sndrcvinfo *srcv + , struct thread *p ) { @@ -10669,6 +10717,7 @@ sctp_lower_sosend(struct socket *so, int got_all_of_the_send = 0; int hold_tcblock = 0; int non_blocking = 0; + int temp_flags = 0; error = 0; net = NULL; @@ -10721,12 +10770,50 @@ sctp_lower_sosend(struct socket *so, } hold_tcblock = 0; SCTP_INP_RUNLOCK(inp); - if (addr) + if (addr) { /* Must locate the net structure if addr given */ net = sctp_findnet(stcb, addr); - else + if (net) { + /* validate port was 0 or correct */ + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addr; + if ((sin->sin_port != 0) && + (sin->sin_port != stcb->rport)) { + net = NULL; + } + } + temp_flags |= SCTP_ADDR_OVER; + } else net = stcb->asoc.primary_destination; + if (addr && (net == NULL)) { + /* Could not find address, was it legal */ + if (addr->sa_family == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addr; + if (sin->sin_addr.s_addr == 0) { + if ((sin->sin_port == 0) || + (sin->sin_port == stcb->rport)) { + net = stcb->asoc.primary_destination; + } + } + } else { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if ((sin6->sin6_port == 0) || + (sin6->sin6_port == stcb->rport)) { + net = stcb->asoc.primary_destination; + } + } + } + } + if (net == NULL) { + error = EINVAL; + goto out_unlocked; + } } else if (use_rcvinfo && srcv && srcv->sinfo_assoc_id) { stcb = sctp_findassociation_ep_asocid(inp, srcv->sinfo_assoc_id, 0); if (stcb) { @@ -10738,6 +10825,21 @@ sctp_lower_sosend(struct socket *so, net = sctp_findnet(stcb, addr); else net = stcb->asoc.primary_destination; + if ((srcv->sinfo_flags & SCTP_ADDR_OVER) && + ((net == NULL) || (addr == NULL))) { + struct sockaddr_in *sin; + + if (addr == NULL) { + error = EINVAL; + goto out_unlocked; + } + sin = (struct sockaddr_in *)addr; + /* Validate port is 0 or correct */ + if ((sin->sin_port != 0) && + (sin->sin_port != stcb->rport)) { + net = NULL; + } + } } hold_tcblock = 0; } else if (addr) { @@ -11010,13 +11112,14 @@ sctp_lower_sosend(struct socket *so, p->td_proc->p_stats->p_ru.ru_msgsnd++; } if (stcb) { - if (net && ((srcv->sinfo_flags & SCTP_ADDR_OVER))) { - /* we take the override or the unconfirmed */ - ; - } else { + if (((srcv->sinfo_flags | temp_flags) & SCTP_ADDR_OVER) == 0) { net = stcb->asoc.primary_destination; } } + if (net == NULL) { + error = EINVAL; + goto out_unlocked; + } if ((net->flight_size > net->cwnd) && (sctp_cmt_on_off == 0)) { /*- * CMT: Added check for CMT above. net above is the primary @@ -11389,7 +11492,15 @@ sctp_lower_sosend(struct socket *so, un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk))); - queue_only = 0; + if (net->flight_size > (net->mtu * stcb->asoc.max_burst)) { + queue_only = 1; + SCTP_STAT_INCR(sctps_send_burst_avoid); + } else if (net->flight_size > net->cwnd) { + queue_only = 1; + SCTP_STAT_INCR(sctps_send_cwnd_avoid); + } else { + queue_only = 0; + } } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && @@ -11646,7 +11757,15 @@ skip_out_eof: un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk))); - queue_only = 0; + if (net->flight_size > (net->mtu * stcb->asoc.max_burst)) { + queue_only = 1; + SCTP_STAT_INCR(sctps_send_burst_avoid); + } else if (net->flight_size > net->cwnd) { + queue_only = 1; + SCTP_STAT_INCR(sctps_send_cwnd_avoid); + } else { + queue_only = 0; + } } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h index 584252a..2f9b344 100644 --- a/sys/netinet/sctp_output.h +++ b/sys/netinet/sctp_output.h @@ -75,7 +75,8 @@ void sctp_send_initiate(struct sctp_inpcb *, struct sctp_tcb *); void sctp_send_initiate_ack(struct sctp_inpcb *, struct sctp_tcb *, - struct mbuf *, int, int, struct sctphdr *, struct sctp_init_chunk *); + struct mbuf *, int, int, struct sctphdr *, struct sctp_init_chunk *, + uint32_t, uint32_t); struct mbuf * sctp_arethere_unrecognized_parameters(struct mbuf *, int, int *, @@ -98,7 +99,9 @@ int sctp_send_shutdown_ack(struct sctp_tcb *, struct sctp_nets *); int sctp_send_shutdown_complete(struct sctp_tcb *, struct sctp_nets *); -int sctp_send_shutdown_complete2(struct mbuf *, int, struct sctphdr *); +int +sctp_send_shutdown_complete2(struct mbuf *, int, struct sctphdr *, + uint32_t, uint32_t); int sctp_send_asconf(struct sctp_tcb *, struct sctp_nets *); @@ -116,7 +119,6 @@ int sctp_output(struct sctp_inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct thread *, int); - void sctp_insert_on_wheel(struct sctp_tcb *stcb, struct sctp_association *asoc, @@ -176,9 +178,11 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, void sctp_send_abort(struct mbuf *, int, struct sctphdr *, uint32_t, - struct mbuf *); + struct mbuf *, uint32_t, uint32_t); -void sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t); +void +sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t, uint32_t, + uint32_t); int sctp_sosend(struct socket *so, diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 31c4bc0..75af8b3 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -130,13 +130,13 @@ sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb) */ struct sctp_vrf * -sctp_allocate_vrf(int vrfid) +sctp_allocate_vrf(int vrf_id) { struct sctp_vrf *vrf = NULL; struct sctp_vrflist *bucket; /* First allocate the VRF structure */ - vrf = sctp_find_vrf(vrfid); + vrf = sctp_find_vrf(vrf_id); if (vrf) { /* Already allocated */ return (vrf); @@ -146,28 +146,38 @@ sctp_allocate_vrf(int vrfid) if (vrf == NULL) { /* No memory */ #ifdef INVARIANTS - panic("No memory for VRF:%d", vrfid); + panic("No memory for VRF:%d", vrf_id); #endif return (NULL); } /* setup the VRF */ memset(vrf, 0, sizeof(struct sctp_vrf)); - vrf->vrf_id = vrfid; + vrf->vrf_id = vrf_id; LIST_INIT(&vrf->ifnlist); vrf->total_ifa_count = 0; /* Init the HASH of addresses */ - vrf->vrf_addr_hash = SCTP_HASH_INIT(SCTP_VRF_HASH_SIZE, - &vrf->vrf_hashmark); + vrf->vrf_addr_hash = SCTP_HASH_INIT(SCTP_VRF_ADDR_HASH_SIZE, + &vrf->vrf_addr_hashmark); if (vrf->vrf_addr_hash == NULL) { /* No memory */ #ifdef INVARIANTS - panic("No memory for VRF:%d", vrfid); + panic("No memory for VRF:%d", vrf_id); +#endif + return (NULL); + } + vrf->vrf_ifn_hash = SCTP_HASH_INIT(SCTP_VRF_IFN_HASH_SIZE, + &vrf->vrf_ifn_hashmark); + if (vrf->vrf_ifn_hash == NULL) { + /* No memory */ +#ifdef INVARIANTS + panic("No memory for VRF:%d", vrf_id); #endif return (NULL); } /* Add it to the hash table */ - bucket = &sctppcbinfo.sctp_vrfhash[(vrfid & sctppcbinfo.hashvrfmark)]; + bucket = &sctppcbinfo.sctp_vrfhash[(vrf_id & sctppcbinfo.hashvrfmark)]; LIST_INSERT_HEAD(bucket, vrf, next_vrf); + atomic_add_int(&sctppcbinfo.ipi_count_vrfs, 1); return (vrf); } @@ -176,12 +186,14 @@ struct sctp_ifn * sctp_find_ifn(struct sctp_vrf *vrf, void *ifn, uint32_t ifn_index) { struct sctp_ifn *sctp_ifnp; + struct sctp_ifnlist *hash_ifn_head; /* * We assume the lock is held for the addresses if thats wrong * problems could occur :-) */ - LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) { + hash_ifn_head = &vrf->vrf_ifn_hash[(ifn_index & vrf->vrf_ifn_hashmark)]; + LIST_FOREACH(sctp_ifnp, hash_ifn_head, next_bucket) { if (sctp_ifnp->ifn_index == ifn_index) { return (sctp_ifnp); } @@ -192,15 +204,17 @@ sctp_find_ifn(struct sctp_vrf *vrf, void *ifn, uint32_t ifn_index) return (NULL); } + + struct sctp_vrf * -sctp_find_vrf(uint32_t vrfid) +sctp_find_vrf(uint32_t vrf_id) { struct sctp_vrflist *bucket; struct sctp_vrf *liste; - bucket = &sctppcbinfo.sctp_vrfhash[(vrfid & sctppcbinfo.hashvrfmark)]; + bucket = &sctppcbinfo.sctp_vrfhash[(vrf_id & sctppcbinfo.hashvrfmark)]; LIST_FOREACH(liste, bucket, next_vrf) { - if (vrfid == liste->vrf_id) { + if (vrf_id == liste->vrf_id) { return (liste); } } @@ -209,6 +223,35 @@ sctp_find_vrf(uint32_t vrfid) void +sctp_free_ifn(struct sctp_ifn *sctp_ifnp) +{ + int ret; + + ret = atomic_fetchadd_int(&sctp_ifnp->refcount, -1); + if (ret == 1) { + /* We zero'd the count */ + SCTP_FREE(sctp_ifnp); + atomic_subtract_int(&sctppcbinfo.ipi_count_ifns, 1); + } +} + +void +sctp_update_ifn_mtu(uint32_t vrf_id, uint32_t ifn_index, uint32_t mtu) +{ + struct sctp_ifn *sctp_ifnp; + struct sctp_vrf *vrf; + + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) + return; + sctp_ifnp = sctp_find_ifn(vrf, (void *)NULL, ifn_index); + if (sctp_ifnp != NULL) { + sctp_ifnp->ifn_mtu = mtu; + } +} + + +void sctp_free_ifa(struct sctp_ifa *sctp_ifap) { int ret; @@ -216,37 +259,50 @@ sctp_free_ifa(struct sctp_ifa *sctp_ifap) ret = atomic_fetchadd_int(&sctp_ifap->refcount, -1); if (ret == 1) { /* We zero'd the count */ -#ifdef INVARIANTS - if (sctp_ifap->in_ifa_list) { - panic("Attempt to free item in a list"); - } -#else - if (sctp_ifap->in_ifa_list) { - printf("in_ifa_list was not clear, fixing cnt\n"); - atomic_add_int(&sctp_ifap->refcount, 1); - return; - } -#endif SCTP_FREE(sctp_ifap); + atomic_subtract_int(&sctppcbinfo.ipi_count_ifas, 1); } } +static void +sctp_delete_ifn(struct sctp_ifn *sctp_ifnp, int hold_addr_lock) +{ + struct sctp_ifn *found; + + found = sctp_find_ifn(sctp_ifnp->vrf, sctp_ifnp->ifn_p, sctp_ifnp->ifn_index); + if (found == NULL) { + /* Not in the list.. sorry */ + return; + } + if (hold_addr_lock == 0) + SCTP_IPI_ADDR_LOCK(); + LIST_REMOVE(sctp_ifnp, next_bucket); + LIST_REMOVE(sctp_ifnp, next_ifn); + if (hold_addr_lock == 0) + SCTP_IPI_ADDR_UNLOCK(); + /* Take away the reference, and possibly free it */ + sctp_free_ifn(sctp_ifnp); +} + + struct sctp_ifa * -sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, +sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index, uint32_t ifn_type, const char *if_name, - void *ifa, struct sockaddr *addr, uint32_t ifa_flags, int dynamic_add) + void *ifa, struct sockaddr *addr, uint32_t ifa_flags, + int dynamic_add) { struct sctp_vrf *vrf; struct sctp_ifn *sctp_ifnp = NULL; struct sctp_ifa *sctp_ifap = NULL; - struct sctp_ifalist *hash_head; + struct sctp_ifalist *hash_addr_head; + struct sctp_ifnlist *hash_ifn_head; uint32_t hash_of_addr; /* How granular do we need the locks to be here? */ SCTP_IPI_ADDR_LOCK(); - vrf = sctp_find_vrf(vrfid); + vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { - vrf = sctp_allocate_vrf(vrfid); + vrf = sctp_allocate_vrf(vrf_id); if (vrf == NULL) { SCTP_IPI_ADDR_UNLOCK(); return (NULL); @@ -270,12 +326,20 @@ sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, sctp_ifnp->ifn_p = ifn; sctp_ifnp->ifn_type = ifn_type; sctp_ifnp->ifa_count = 0; - sctp_ifnp->refcount = 0; + sctp_ifnp->refcount = 1; sctp_ifnp->vrf = vrf; - memcpy(sctp_ifnp->ifn_name, if_name, SCTP_IFNAMSIZ); + sctp_ifnp->ifn_mtu = SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index); + if (if_name != NULL) { + memcpy(sctp_ifnp->ifn_name, if_name, SCTP_IFNAMSIZ); + } else { + memcpy(sctp_ifnp->ifn_name, "unknown", min(7, SCTP_IFNAMSIZ)); + } + hash_ifn_head = &vrf->vrf_ifn_hash[(ifn_index & vrf->vrf_ifn_hashmark)]; LIST_INIT(&sctp_ifnp->ifalist); SCTP_IPI_ADDR_LOCK(); + LIST_INSERT_HEAD(hash_ifn_head, sctp_ifnp, next_bucket); LIST_INSERT_HEAD(&vrf->ifnlist, sctp_ifnp, next_ifn); + atomic_add_int(&sctppcbinfo.ipi_count_ifns, 1); } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1); if (sctp_ifap) { @@ -303,7 +367,7 @@ sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, /* repair ifnp which was NULL ? */ sctp_ifap->localifa_flags = SCTP_ADDR_VALID; sctp_ifap->ifn_p = sctp_ifnp; - atomic_add_int(&sctp_ifnp->refcount, 1); + atomic_add_int(&sctp_ifap->ifn_p->refcount, 1); } goto exit_stage_left; } @@ -356,13 +420,13 @@ sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, sctp_ifap->src_is_glob = 1; } SCTP_IPI_ADDR_LOCK(); - hash_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_hashmark)]; - LIST_INSERT_HEAD(hash_head, sctp_ifap, next_bucket); + hash_addr_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)]; + LIST_INSERT_HEAD(hash_addr_head, sctp_ifap, next_bucket); sctp_ifap->refcount = 1; LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa); sctp_ifnp->ifa_count++; - sctp_ifap->in_ifa_list = 1; vrf->total_ifa_count++; + atomic_add_int(&sctppcbinfo.ipi_count_ifas, 1); SCTP_IPI_ADDR_UNLOCK(); if (dynamic_add) { /* @@ -383,9 +447,8 @@ sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, printf("Lost and address change ???\n"); } #endif /* SCTP_DEBUG */ - /* Opps, must decrement the count */ - sctp_free_ifa(sctp_ifap); + sctp_del_addr_from_vrf(vrf_id, addr, ifn_index); return (NULL); } SCTP_INCR_LADDR_COUNT(); @@ -403,12 +466,15 @@ sctp_add_addr_to_vrf(uint32_t vrfid, void *ifn, uint32_t ifn_index, (struct sctp_tcb *)NULL, (struct sctp_nets *)NULL); SCTP_IPI_ITERATOR_WQ_UNLOCK(); + } else { + /* it's ready for use */ + sctp_ifap->localifa_flags &= ~SCTP_ADDR_DEFER_USE; } return (sctp_ifap); } void -sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr, +sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr, uint32_t ifn_index) { struct sctp_vrf *vrf; @@ -416,9 +482,11 @@ sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr, SCTP_IPI_ADDR_LOCK(); - vrf = sctp_find_vrf(vrfid); + vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { - printf("Can't find vrfid:%d\n", vrfid); +#ifdef SCTP_DEBUG + printf("Can't find vrf_id:%d\n", vrf_id); +#endif goto out_now; } sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, 1); @@ -429,13 +497,21 @@ sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr, vrf->total_ifa_count--; LIST_REMOVE(sctp_ifap, next_bucket); LIST_REMOVE(sctp_ifap, next_ifa); - sctp_ifap->in_ifa_list = 0; - atomic_add_int(&sctp_ifap->ifn_p->refcount, -1); - } else { + if (sctp_ifap->ifn_p) { + if (SCTP_LIST_EMPTY(&sctp_ifap->ifn_p->ifalist)) { + sctp_delete_ifn(sctp_ifap->ifn_p, 1); + } + sctp_free_ifn(sctp_ifap->ifn_p); + sctp_ifap->ifn_p = NULL; + } + } +#ifdef SCTP_DEBUG + else { printf("Del Addr-ifn:%d Could not find address:", ifn_index); sctp_print_address(addr); } +#endif out_now: SCTP_IPI_ADDR_UNLOCK(); if (sctp_ifap) { @@ -521,7 +597,7 @@ sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from, continue; } if (lport != inp->sctp_lport) { - SCTP_INP_RUNLOCK(inp); + SCTP_INP_RUNLOCK(inp); continue; } if (inp->def_vrf_id != vrf_id) { @@ -1671,7 +1747,6 @@ sctp_inpcb_alloc(struct socket *so) */ int i, error; struct sctp_inpcb *inp; - struct sctp_pcb *m; struct timeval time; sctp_sharedkey_t *null_key; @@ -1722,14 +1797,12 @@ sctp_inpcb_alloc(struct socket *so) /* UDP style socket */ inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE | SCTP_PCB_FLAGS_UNBOUND); - sctp_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT); /* Be sure it is NON-BLOCKING IO for UDP */ /* SCTP_SET_SO_NBIO(so); */ } else if (SCTP_SO_TYPE(so) == SOCK_STREAM) { /* TCP style socket */ inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE | SCTP_PCB_FLAGS_UNBOUND); - sctp_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT); /* Be sure we have blocking IO by default */ SCTP_CLEAR_SO_NBIO(so); } else { @@ -1748,6 +1821,7 @@ sctp_inpcb_alloc(struct socket *so) return (ENOBUFS); } inp->def_vrf_id = SCTP_DEFAULT_VRFID; + inp->def_table_id = SCTP_DEFAULT_TABLEID; SCTP_INP_INFO_WLOCK(); SCTP_INP_LOCK_INIT(inp); @@ -2882,7 +2956,6 @@ sctp_set_initial_cc_param(struct sctp_tcb *stcb, struct sctp_nets *net) net->ssthresh = stcb->asoc.peers_rwnd; } - int sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, int set_scope, int from) @@ -3062,7 +3135,8 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, (void)sa6_embedscope(sin6, ip6_use_defzone); sin6->sin6_scope_id = 0; } - rtalloc_ign((struct route *)&net->ro, 0UL); + SCTP_RTALLOC((sctp_route_t *) & net->ro, stcb->asoc.vrf_id, + stcb->asoc.table_id); if (newaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; @@ -3070,19 +3144,64 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; (void)sa6_recoverscope(sin6); } - if ((net->ro.ro_rt) && - (net->ro.ro_rt->rt_ifp)) { - net->mtu = net->ro.ro_rt->rt_ifp->if_mtu; + if (SCTP_ROUTE_HAS_VALID_IFN(&net->ro)) { + /* Get source address */ + net->ro._s_addr = sctp_source_address_selection(stcb->sctp_ep, + stcb, + (sctp_route_t *) & net->ro, + net, + 0, + stcb->asoc.vrf_id); + /* Now get the interface MTU */ + if (net->ro._s_addr && net->ro._s_addr->ifn_p) { + net->mtu = SCTP_GATHER_MTU_FROM_INTFC(net->ro._s_addr->ifn_p); + } else { + net->mtu = 0; + } +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("We have found an interface mtu of %d\n", net->mtu); +#endif + if (net->mtu == 0) { + /* Huh ?? */ + net->mtu = SCTP_DEFAULT_MTU; + } else { + uint32_t rmtu; + + rmtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, net->ro.ro_rt); +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("The route mtu is %d\n", rmtu); +#endif + if (rmtu == 0) { + /* + * Start things off to match mtu of + * interface please. + */ + SCTP_SET_MTU_OF_ROUTE(&net->ro._l_addr.sa, + net->ro.ro_rt, net->mtu); + } else { + /* + * we take the route mtu over the interface, + * since the route may be leading out the + * loopback, or a different interface. + */ + net->mtu = rmtu; + } + } if (from == SCTP_ALLOC_ASOC) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("New assoc sets mtu to :%d\n", + net->mtu); +#endif stcb->asoc.smallest_mtu = net->mtu; } - /* start things off to match mtu of interface please. */ - net->ro.ro_rt->rt_rmx.rmx_mtu = net->ro.ro_rt->rt_ifp->if_mtu; } else { net->mtu = stcb->asoc.smallest_mtu; } - if (stcb->asoc.smallest_mtu > net->mtu) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("new address mtu:%d smaller than smallest:%d\n", + net->mtu, stcb->asoc.smallest_mtu); +#endif stcb->asoc.smallest_mtu = net->mtu; } /* @@ -3187,7 +3306,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, */ struct sctp_tcb * sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, - int for_a_init, int *error, uint32_t override_tag, uint32_t vrf) + int for_a_init, int *error, uint32_t override_tag, uint32_t vrf_id) { struct sctp_tcb *stcb; struct sctp_association *asoc; @@ -3287,7 +3406,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, /* setup back pointer's */ stcb->sctp_ep = inp; stcb->sctp_socket = inp->sctp_socket; - if ((err = sctp_init_asoc(inp, asoc, for_a_init, override_tag, vrf))) { + if ((err = sctp_init_asoc(inp, asoc, for_a_init, override_tag, vrf_id))) { /* failed */ SCTP_TCB_LOCK_DESTROY(stcb); SCTP_TCB_SEND_LOCK_DESTROY(stcb); @@ -3745,7 +3864,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre SOCK_UNLOCK(so); sctp_sowwakeup(inp, so); sctp_sorwakeup(inp, so); - wakeup(&so->so_timeo); + SCTP_SOWAKEUP(so); } } } @@ -4295,7 +4414,7 @@ sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa) if (net->ro._s_addr && (net->ro._s_addr->ifa == laddr->ifa)) { /* Yep, purge src address selected */ - struct rtentry *rt; + sctp_rtentry_t *rt; /* delete this address if cached */ rt = net->ro.ro_rt; @@ -4732,6 +4851,10 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, /* Skip multi-cast addresses */ goto next_param; } + if ((sin.sin_addr.s_addr == INADDR_BROADCAST) || + (sin.sin_addr.s_addr == INADDR_ANY)) { + goto next_param; + } sa = (struct sockaddr *)&sin; inp = stcb->sctp_ep; atomic_add_int(&stcb->asoc.refcnt, 1); diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 85e5dc6..077d345 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -51,7 +51,6 @@ TAILQ_HEAD(sctp_readhead, sctp_queued_to_read); TAILQ_HEAD(sctp_streamhead, sctp_stream_queue_pending); #include <netinet/sctp_structs.h> -#include <netinet/sctp_uio.h> #include <netinet/sctp_auth.h> #define SCTP_PCBHASH_ALLADDR(port, mask) (port & mask) @@ -60,17 +59,21 @@ TAILQ_HEAD(sctp_streamhead, sctp_stream_queue_pending); struct sctp_vrf { LIST_ENTRY(sctp_vrf) next_vrf; struct sctp_ifalist *vrf_addr_hash; + struct sctp_ifnlist *vrf_ifn_hash; struct sctp_ifnlist ifnlist; uint32_t vrf_id; uint32_t total_ifa_count; - u_long vrf_hashmark; + u_long vrf_addr_hashmark; + u_long vrf_ifn_hashmark; }; struct sctp_ifn { struct sctp_ifalist ifalist; struct sctp_vrf *vrf; LIST_ENTRY(sctp_ifn) next_ifn; + LIST_ENTRY(sctp_ifn) next_bucket; void *ifn_p; /* never access without appropriate lock */ + uint32_t ifn_mtu; uint32_t ifn_type; uint32_t ifn_index; /* shorthand way to look at ifn for reference */ uint32_t refcount; /* number of reference held should be >= @@ -105,7 +108,8 @@ struct sctp_ifa { uint8_t src_is_loop; uint8_t src_is_priv; uint8_t src_is_glob; - uint8_t in_ifa_list; + uint8_t resv; + }; struct sctp_laddr { @@ -197,10 +201,20 @@ struct sctp_epinfo { /* socket queue zone info */ uint32_t ipi_count_strmoq; + /* Number of vrfs */ + uint32_t ipi_count_vrfs; + + /* Number of ifns */ + uint32_t ipi_count_ifns; + + /* Number of ifas */ + uint32_t ipi_count_ifas; + /* system wide number of free chunks hanging around */ uint32_t ipi_free_chunks; uint32_t ipi_free_strmoq; + struct sctpvtaghead vtag_timewait[SCTP_STACK_VTAG_HASH_SIZE]; /* address work queue handling */ @@ -345,6 +359,7 @@ struct sctp_inpcb { struct mtx inp_rdata_mtx; int32_t refcount; uint32_t def_vrf_id; + uint32_t def_table_id; uint32_t total_sends; uint32_t total_recvs; uint32_t last_abort_code; @@ -405,15 +420,18 @@ sctp_add_addr_to_vrf(uint32_t vrfid, const char *if_name, void *ifa, struct sockaddr *addr, uint32_t ifa_flags, int dynamic_add); +void sctp_update_ifn_mtu(uint32_t vrf_id, uint32_t ifn_index, uint32_t mtu); + +void sctp_free_ifn(struct sctp_ifn *sctp_ifnp); void sctp_free_ifa(struct sctp_ifa *sctp_ifap); -void + +void sctp_del_addr_from_vrf(uint32_t vrfid, struct sockaddr *addr, uint32_t ifn_index); - struct sctp_nets *sctp_findnet(struct sctp_tcb *, struct sockaddr *); struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t); diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index fc6588b..b0be404 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -38,7 +38,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/sctp_os.h> #include <netinet/sctp_header.h> -#include <netinet/sctp_uio.h> #include <netinet/sctp_auth.h> struct sctp_timer { @@ -157,6 +156,12 @@ struct sctp_asconf_iterator { }; +struct sctp_net_route { + sctp_rtentry_t *ro_rt; + union sctp_sockstore _l_addr; /* remote peer addr */ + struct sctp_ifa *_s_addr; /* our selected src addr */ +}; + struct sctp_nets { TAILQ_ENTRY(sctp_nets) sctp_next; /* next link */ @@ -170,11 +175,8 @@ struct sctp_nets { * The following two in combination equate to a route entry for v6 * or v4. */ - struct sctp_route { - struct rtentry *ro_rt; - union sctp_sockstore _l_addr; /* remote peer addr */ - struct sctp_ifa *_s_addr; /* our selected src addr */ - } ro; + struct sctp_net_route ro; + /* mtu discovered so far */ uint32_t mtu; uint32_t ssthresh; /* not sure about this one for split */ @@ -368,6 +370,8 @@ struct sctp_queued_to_read { /* sinfo structure Pluse more */ struct mbuf *data; /* front of the mbuf chain of data with * PKT_HDR */ struct mbuf *tail_mbuf; /* used for multi-part data */ + struct mbuf *aux_data; /* used to hold/cache control if o/s does not + * take it from us */ struct sctp_tcb *stcb; /* assoc, used for window update */ TAILQ_ENTRY(sctp_queued_to_read) next; uint16_t port_from; @@ -585,6 +589,7 @@ struct sctp_association { struct sctp_readhead pending_reply_queue; uint32_t vrf_id; + uint32_t table_id; uint32_t cookie_preserve_req; /* ASCONF next seq I am sending out, inits at init-tsn */ @@ -594,11 +599,9 @@ struct sctp_association { /* next seq I am sending in str reset messages */ uint32_t str_reset_seq_out; - /* next seq I am expecting in str reset messages */ uint32_t str_reset_seq_in; - /* various verification tag information */ uint32_t my_vtag; /* The tag to be used. if assoc is re-initited * by remote end, and I have unlocked this diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c index 943399e..07ec1b1 100644 --- a/sys/netinet/sctp_sysctl.c +++ b/sys/netinet/sctp_sysctl.c @@ -194,6 +194,7 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) 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; SCTP_INP_INCR_REF(inp); SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); @@ -249,6 +250,7 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) xstcb.highest_tsn = stcb->asoc.sending_seq - 1; xstcb.cumulative_tsn = stcb->asoc.last_acked_seq; xstcb.cumulative_tsn_ack = stcb->asoc.cumulative_tsn; + xstcb.mtu = stcb->asoc.smallest_mtu; SCTP_INP_RUNLOCK(inp); SCTP_INP_INFO_RUNLOCK(); error = SYSCTL_OUT(req, &xstcb, sizeof(struct xsctp_tcb)); @@ -268,6 +270,7 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) xraddr.RemAddrCwnd = net->cwnd; xraddr.RemAddrFlightSize = net->flight_size; xraddr.RemAddrStartTime = net->start_time; + xraddr.RemAddrMTU = net->mtu; error = SYSCTL_OUT(req, &xraddr, sizeof(struct xsctp_raddr)); if (error) { atomic_add_int(&stcb->asoc.refcnt, -1); diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index 57ad30d..3e72cc0 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -200,7 +200,6 @@ sctp_audit_retranmission_queue(struct sctp_association *asoc) #endif /* SCTP_DEBUG */ } - int sctp_threshold_management(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net, uint16_t threshold) @@ -354,20 +353,10 @@ sctp_find_alternate_net(struct sctp_tcb *stcb, alt = TAILQ_FIRST(&stcb->asoc.nets); } if (alt->ro.ro_rt == NULL) { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)&alt->ro._l_addr; - if (sin6->sin6_family == AF_INET6) { - (void)sa6_embedscope(sin6, ip6_use_defzone); - } - rtalloc_ign((struct route *)&alt->ro, 0UL); - - if (sin6->sin6_family == AF_INET6) { - (void)sa6_recoverscope(sin6); - } if (alt->ro._s_addr) { sctp_free_ifa(alt->ro._s_addr); alt->ro._s_addr = NULL; + } alt->src_addr_selected = 0; } @@ -936,6 +925,22 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) { /* Move all pending over too */ sctp_move_all_chunks_to_alt(stcb, net, alt); + + /* + * Get the address that failed, to force a new src address + * selecton and a route allocation. + */ + if (net->ro._s_addr) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + } + net->src_addr_selected = 0; + + /* Force a route allocation too */ + if (net->ro.ro_rt) { + RTFREE(net->ro.ro_rt); + net->ro.ro_rt = NULL; + } /* Was it our primary? */ if ((stcb->asoc.primary_destination == net) && (alt != net)) { /* @@ -949,11 +954,6 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, (struct sockaddr *)NULL, alt) == 0) { net->dest_state |= SCTP_ADDR_WAS_PRIMARY; - if (net->ro._s_addr) { - sctp_free_ifa(net->ro._s_addr); - net->ro._s_addr = NULL; - } - net->src_addr_selected = 0; } } } @@ -1538,16 +1538,27 @@ sctp_pathmtu_timer(struct sctp_inpcb *inp, if (next_mtu <= net->mtu) { /* nothing to do */ return; - } - if (net->ro.ro_rt != NULL) { - /* - * only if we have a route and interface do we set anything. - * Note we always restart the timer though just in case it - * is updated (i.e. the ifp) or route/ifp is populated. - */ - if (net->ro.ro_rt->rt_ifp != NULL) { - if (net->ro.ro_rt->rt_ifp->if_mtu > next_mtu) { - /* ok it will fit out the door */ + } { + uint32_t mtu; + + if ((net->src_addr_selected == 0) || + (net->ro._s_addr == NULL) || + (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { + if ((net->ro._s_addr == NULL) && (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + } + net->ro._s_addr = sctp_source_address_selection(inp, + stcb, + (sctp_route_t *) & net->ro, + net, 0, stcb->asoc.vrf_id); + if (net->ro._s_addr) + net->src_addr_selected = 1; + } + if (net->ro._s_addr) { + mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._s_addr.sa, net->ro.ro_rt); + if (mtu > next_mtu) { net->mtu = next_mtu; } } diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 1e54e9c..240db62 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -895,6 +895,10 @@ struct sctpstat { u_long sctps_cached_strmoq; /* Number of cached stream oq's used */ u_long sctps_left_abandon; /* Number of unread message abandonded * by close */ + u_long sctps_send_burst_avoid; /* Send burst avoidance, already max + * burst inflight to net */ + u_long sctps_send_cwnd_avoid; /* Send cwnd full avoidance, already + * max burst inflight to net */ }; #define SCTP_STAT_INCR(_x) SCTP_STAT_INCR_BY(_x,1) @@ -929,6 +933,7 @@ struct xsctp_inpcb { uint32_t total_sends; uint32_t total_recvs; uint32_t total_nospaces; + uint32_t fragmentation_point; /* add more endpoint specific data here */ }; @@ -955,6 +960,7 @@ struct xsctp_tcb { uint32_t highest_tsn; uint32_t cumulative_tsn; uint32_t cumulative_tsn_ack; + uint32_t mtu; /* add more association specific data here */ uint16_t number_local_addresses; uint16_t number_remote_addresses; @@ -977,6 +983,7 @@ struct xsctp_raddr { uint32_t RemAddrErrorCounter; /* */ uint32_t RemAddrCwnd; /* */ uint32_t RemAddrFlightSize; /* */ + uint32_t RemAddrMTU; /* */ struct timeval RemAddrStartTime; /* sctpAssocLocalRemEntry 8 */ /* add more remote address specific data */ }; @@ -989,8 +996,7 @@ int sctp_lower_sosend(struct socket *so, struct sockaddr *addr, struct uio *uio, - - struct mbuf *top, + struct mbuf *i_pak, struct mbuf *control, int flags, int use_rcvinfo, @@ -1008,7 +1014,6 @@ sctp_sorecvmsg(struct socket *so, struct sctp_sndrcvinfo *sinfo, int filling_sinfo); - #endif /* @@ -1027,7 +1032,7 @@ int sctp_getladdrs __P((int, sctp_assoc_t, struct sockaddr **)); void sctp_freeladdrs __P((struct sockaddr *)); int sctp_opt_info __P((int, sctp_assoc_t, int, void *, socklen_t *)); -ssize_t sctp_sendmsg +ssize_t sctp_sendmsg __P((int, const void *, size_t, const struct sockaddr *, socklen_t, uint32_t, uint32_t, uint16_t, uint32_t, uint32_t)); @@ -1035,17 +1040,15 @@ __P((int, const void *, size_t, ssize_t sctp_send __P((int sd, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags)); - ssize_t - sctp_sendx __P((int sd, const void *msg, size_t len, + ssize_t sctp_sendx __P((int sd, const void *msg, size_t len, struct sockaddr *addrs, int addrcnt, struct sctp_sndrcvinfo *sinfo, int flags)); - ssize_t - sctp_sendmsgx __P((int sd, const void *, size_t, + + ssize_t sctp_sendmsgx __P((int sd, const void *, size_t, struct sockaddr *, int, uint32_t, uint32_t, uint16_t, uint32_t, uint32_t)); -sctp_assoc_t -sctp_getassocid __P((int sd, struct sockaddr *sa)); + sctp_assoc_t sctp_getassocid __P((int sd, struct sockaddr *sa)); ssize_t sctp_recvmsg __P((int, void *, size_t, struct sockaddr *, socklen_t *, struct sctp_sndrcvinfo *, int *)); diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index c187933..a37da5c 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -110,7 +110,10 @@ sctp_pathmtu_adjustment(struct sctp_inpcb *inp, /* Adjust that too */ stcb->asoc.smallest_mtu = nxtsz; /* now off to subtract IP_DF flag if needed */ - +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_pathmtu_adjust called inp:%p stcb:%p net:%p nxtsz:%d\n", + inp, stcb, net, nxtsz); +#endif TAILQ_FOREACH(chk, &stcb->asoc.send_queue, sctp_next) { if ((chk->send_size + IP_HDR_SIZE) > nxtsz) { chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; @@ -201,6 +204,10 @@ sctp_notify_mbuf(struct sctp_inpcb *inp, } /* now what about the ep? */ if (stcb->asoc.smallest_mtu > nxtsz) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("notify_mbuf (ICMP) calls sctp_pathmtu_adjust mtu:%d\n", + nxtsz); +#endif sctp_pathmtu_adjustment(inp, stcb, net, nxtsz); } if (tmr_stopped) @@ -513,10 +520,10 @@ sctp_attach(struct socket *so, int proto, struct thread *p) #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 15); #endif - SCTP_INP_WUNLOCK(inp); + SCTP_INP_WUNLOCK(inp); sctp_inpcb_free(inp, 1, 0); } else { - SCTP_INP_WUNLOCK(inp); + SCTP_INP_WUNLOCK(inp); } return error; } @@ -1234,7 +1241,6 @@ sctp_count_max_addresses(struct sctp_inpcb *inp) return (cnt); } - static int sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, size_t optsize, void *p, int delay) @@ -1243,8 +1249,8 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, int creat_lock_on = 0; struct sctp_tcb *stcb = NULL; struct sockaddr *sa; - int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr, i; - size_t incr, at; + int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr; + int added = 0; uint32_t vrf_id; sctp_assoc_t *a_id; @@ -1281,43 +1287,15 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, totaddrp = (int *)optval; totaddr = *totaddrp; sa = (struct sockaddr *)(totaddrp + 1); - at = incr = 0; - /* account and validate addresses */ - for (i = 0; i < totaddr; i++) { - if (sa->sa_family == AF_INET) { - num_v4++; - incr = sizeof(struct sockaddr_in); - } else if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)sa; - if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - /* Must be non-mapped for connectx */ - error = EINVAL; - goto out_now; - } - num_v6++; - incr = sizeof(struct sockaddr_in6); - } else { - totaddr = i; - break; - } - stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL); - if (stcb != NULL) { - /* Already have or am bring up an association */ - SCTP_ASOC_CREATE_UNLOCK(inp); - creat_lock_on = 0; - SCTP_TCB_UNLOCK(stcb); - error = EALREADY; - goto out_now; - } - if ((at + incr) > optsize) { - totaddr = i; - break; - } - sa = (struct sockaddr *)((caddr_t)sa + incr); + stcb = sctp_connectx_helper_find(inp, sa, &totaddr, &num_v4, &num_v6, &error, (optsize - sizeof(int))); + if (stcb != NULL) { + /* Already have or am bring up an association */ + SCTP_ASOC_CREATE_UNLOCK(inp); + creat_lock_on = 0; + SCTP_TCB_UNLOCK(stcb); + error = EALREADY; + goto out_now; } - sa = (struct sockaddr *)(totaddrp + 1); #ifdef INET6 if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) && (num_v6 > 0)) { @@ -1356,33 +1334,14 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval, /* Gak! no memory */ goto out_now; } + stcb->asoc.state = SCTP_STATE_COOKIE_WAIT; /* move to second address */ if (sa->sa_family == AF_INET) sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in)); else sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6)); - for (i = 1; i < totaddr; i++) { - if (sa->sa_family == AF_INET) { - incr = sizeof(struct sockaddr_in); - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { - /* assoc gone no un-lock */ - sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_7); - error = ENOBUFS; - goto out_now; - } - } else if (sa->sa_family == AF_INET6) { - incr = sizeof(struct sockaddr_in6); - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { - /* assoc gone no un-lock */ - sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_8); - error = ENOBUFS; - goto out_now; - } - } - sa = (struct sockaddr *)((caddr_t)sa + incr); - } - stcb->asoc.state = SCTP_STATE_COOKIE_WAIT; + added = sctp_connectx_helper_add(stcb, sa, (totaddr - 1), &error); /* Fill in the return id */ a_id = (sctp_assoc_t *) optval; *a_id = sctp_get_associd(stcb); @@ -3087,8 +3046,13 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } if (paddrp->spp_pathmtu > SCTP_DEFAULT_MINSEGMENT) { net->mtu = paddrp->spp_pathmtu; - if (net->mtu < stcb->asoc.smallest_mtu) + if (net->mtu < stcb->asoc.smallest_mtu) { +#ifdef SCTP_PRINT_FOR_B_AND_M + 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) { diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h index b5f9ad4..ae68b23 100644 --- a/sys/netinet/sctp_var.h +++ b/sys/netinet/sctp_var.h @@ -296,9 +296,6 @@ __P((struct sctp_inpcb *, int, struct sctphdr *, /* can't use sctp_assoc_t here */ int sctp_peeloff(struct socket *, struct socket *, int, caddr_t, int *); - sctp_assoc_t sctp_getassocid(struct sockaddr *); - - int sctp_ingetaddr(struct socket *, struct sockaddr ** ); diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index a489184..c9b422e 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -837,7 +837,7 @@ sctp_fill_random_store(struct sctp_pcb *m) * our counter. The result becomes our good random numbers and we * then setup to give these out. Note that we do no locking to * protect this. This is ok, since if competing folks call this we - * will get more gobbled gook in the random store whic is what we + * will get more gobbled gook in the random store which is what we * want. There is a danger that two guys will use the same random * numbers, but thats ok too since that is random as well :-> */ @@ -1025,6 +1025,10 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_association *asoc, asoc->peers_rwnd = SCTP_SB_LIMIT_RCV(m->sctp_socket); asoc->smallest_mtu = m->sctp_frag_point; +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("smallest_mtu init'd with asoc to :%d\n", + asoc->smallest_mtu); +#endif asoc->minrto = m->sctp_ep.sctp_minrto; asoc->maxrto = m->sctp_ep.sctp_maxrto; @@ -2424,6 +2428,10 @@ sctp_mtu_size_reset(struct sctp_inpcb *inp, struct sctp_tmit_chunk *chk; unsigned int eff_mtu, ovh; +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset(%p, asoc:%p mtu:%d\n", + inp, asoc, mtu); +#endif asoc->smallest_mtu = mtu; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { ovh = SCTP_MIN_OVERHEAD; @@ -3207,6 +3215,15 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb, /* Can't send up to a closed socket any notifications */ return; } + if (stcb && ((stcb->asoc.state & SCTP_STATE_COOKIE_WAIT) || + (stcb->asoc.state & SCTP_STATE_COOKIE_ECHOED))) { + if ((notification == SCTP_NOTIFY_INTERFACE_DOWN) || + (notification == SCTP_NOTIFY_INTERFACE_UP) || + (notification == SCTP_NOTIFY_INTERFACE_CONFIRMED)) { + /* Don't report these in front states */ + return; + } + } if (stcb && (stcb->asoc.assoc_up_sent == 0) && (notification != SCTP_NOTIFY_ASSOC_UP)) { if ((notification != SCTP_NOTIFY_ASSOC_DOWN) && (notification != SCTP_NOTIFY_ASSOC_ABORTED) && @@ -3478,7 +3495,8 @@ sctp_abort_notification(struct sctp_tcb *stcb, int error) void sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb, - struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err) + struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err, + uint32_t vrf_id, uint32_t table_id) { uint32_t vtag; @@ -3487,8 +3505,11 @@ sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb, /* We have a TCB to abort, send notification too */ vtag = stcb->asoc.peer_vtag; sctp_abort_notification(stcb, 0); + /* get the assoc vrf id and table id */ + vrf_id = stcb->asoc.vrf_id; + table_id = stcb->asoc.table_id; } - sctp_send_abort(m, iphlen, sh, vtag, op_err); + sctp_send_abort(m, iphlen, sh, vtag, op_err, vrf_id, table_id); if (stcb != NULL) { /* Ok, now lets free it */ sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTPUTIL + SCTP_LOC_4); @@ -3597,7 +3618,8 @@ sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, - struct sctp_inpcb *inp, struct mbuf *op_err) + struct sctp_inpcb *inp, struct mbuf *op_err, uint32_t vrf_id, + uint32_t table_id) { struct sctp_chunkhdr *ch, chunk_buf; unsigned int chk_length; @@ -3631,7 +3653,8 @@ sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, */ return; case SCTP_SHUTDOWN_ACK: - sctp_send_shutdown_complete2(m, iphlen, sh); + sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, + table_id); return; default: break; @@ -3640,7 +3663,7 @@ sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh, ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); } - sctp_send_abort(m, iphlen, sh, 0, op_err); + sctp_send_abort(m, iphlen, sh, 0, op_err, vrf_id, table_id); } /* @@ -3996,7 +4019,10 @@ sctp_add_to_readq(struct sctp_inpcb *inp, TAILQ_INSERT_TAIL(&inp->read_queue, control, next); SCTP_INP_READ_UNLOCK(inp); if (inp && inp->sctp_socket) { - sctp_sorwakeup(inp, inp->sctp_socket); + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { + SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); + } else + sctp_sorwakeup(inp, inp->sctp_socket); } } @@ -4114,7 +4140,10 @@ get_out: SCTP_INP_READ_UNLOCK(inp); } if (inp && inp->sctp_socket) { - sctp_sorwakeup(inp, inp->sctp_socket); + if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_ZERO_COPY_ACTIVE)) { + SCTP_ZERO_COPY_EVENT(inp, inp->sctp_socket); + } else + sctp_sorwakeup(inp, inp->sctp_socket); } return (0); } @@ -4333,11 +4362,11 @@ sctp_find_ifa_by_addr(struct sockaddr *addr, uint32_t vrf_id, int holds_lock) } hash_of_addr = sctp_get_ifa_hash_val(addr); - hash_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_hashmark)]; + hash_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)]; if (hash_head == NULL) { printf("hash_of_addr:%x mask:%x table:%x - ", - (u_int)hash_of_addr, (u_int)vrf->vrf_hashmark, - (u_int)(hash_of_addr & vrf->vrf_hashmark)); + (u_int)hash_of_addr, (u_int)vrf->vrf_addr_hashmark, + (u_int)(hash_of_addr & vrf->vrf_addr_hashmark)); sctp_print_address(addr); printf("No such bucket for address\n"); if (holds_lock == 0) @@ -4519,6 +4548,9 @@ sctp_sorecvmsg(struct socket *so, int held_length = 0; int sockbuf_lock = 0; + if (uio == NULL) { + return (EINVAL); + } if (msg_flags) { in_flags = *msg_flags; if (in_flags & MSG_PEEK) @@ -4526,8 +4558,8 @@ sctp_sorecvmsg(struct socket *so, } else { in_flags = 0; } - if (uio) - slen = uio->uio_resid; + slen = uio->uio_resid; + /* Pull in and set up our int flags */ if (in_flags & MSG_OOB) { /* Out of band's NOT supported */ @@ -4554,11 +4586,11 @@ sctp_sorecvmsg(struct socket *so, in_eeor_mode = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); #ifdef SCTP_RECV_RWND_LOGGING sctp_misc_ints(SCTP_SORECV_ENTER, - rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, slen); + rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, uio->uio_resid); #endif #ifdef SCTP_RECV_RWND_LOGGING sctp_misc_ints(SCTP_SORECV_ENTERPL, - rwnd_req, block_allowed, so->so_rcv.sb_cc, slen); + rwnd_req, block_allowed, so->so_rcv.sb_cc, uio->uio_resid); #endif @@ -4593,7 +4625,7 @@ restart_nosblocks: /* we need to wait for data */ #ifdef SCTP_RECV_DETAIL_RWND_LOGGING sctp_misc_ints(SCTP_SORECV_BLOCKSA, - 0, 0, so->so_rcv.sb_cc, slen); + 0, 0, so->so_rcv.sb_cc, uio->uio_resid); #endif if ((so->so_rcv.sb_cc == 0) && ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || @@ -4768,7 +4800,7 @@ restart_nosblocks: while (ctl) { if ((ctl->stcb != control->stcb) && (ctl->length) && (ctl->some_taken || - (ctl->spec_flags & M_NOTIFICATION) || + (ctl->spec_flags & M_NOTIFICATION) || ((ctl->do_not_ref_stcb == 0) && (ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0))) ) { @@ -4776,7 +4808,8 @@ restart_nosblocks: * If we have a different TCB next, and there is data * present. If we have already taken some (pdapi), OR we can * ref the tcb and no delivery as started on this stream, we - * take it. + * take it. Note we allow a notification on a different + * assoc to be delivered.. */ control = ctl; goto found_one; @@ -4784,7 +4817,7 @@ restart_nosblocks: (ctl->length) && ((ctl->some_taken) || ((ctl->do_not_ref_stcb == 0) && -- ((ctl->spec_flags & M_NOTIFICATION) == 0) && + ((ctl->spec_flags & M_NOTIFICATION) == 0) && (ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0))) ) { /*- @@ -4792,6 +4825,8 @@ restart_nosblocks: * have the strm interleave feature present. Then if we have * taken some (pdapi) or we can refer to tht tcb AND we have * not started a delivery for this stream, we can take it. + * Note we do NOT allow a notificaiton on the same assoc to + * be delivered. */ control = ctl; goto found_one; @@ -4855,8 +4890,8 @@ found_one: stcb->freed_by_sorcv_sincelast = 0; } } - if (stcb && - ((control->spec_flags & M_NOTIFICATION) == 0) && + if (stcb && + ((control->spec_flags & M_NOTIFICATION) == 0) && control->do_not_ref_stcb == 0) { stcb->asoc.strmin[control->sinfo_stream].delivery_started = 1; } @@ -5188,6 +5223,10 @@ get_more_data: control->held_length = 0; wakeup_read_socket = 1; } + if (control->aux_data) { + sctp_m_free(control->aux_data); + control->aux_data = NULL; + } no_rcv_needed = control->do_not_ref_stcb; sctp_free_remote_addr(control->whoFrom); control->data = NULL; @@ -5325,199 +5364,48 @@ wait_some_more: } goto get_more_data; } else { - /* copy out the mbuf chain */ -get_more_data2: - /* - * Do we have a uio, I doubt it if so we grab the size from - * it, if not you get it all + /*- + * Give caller back the mbuf chain, + * store in uio_resid the length */ - if (uio) - cp_len = uio->uio_resid; - else - cp_len = control->length; - - if ((uint32_t) cp_len >= control->length) { - /* easy way */ - if ((control->end_added == 0) || - (TAILQ_NEXT(control, next) == NULL)) { - /* Need to get rlock */ - if (hold_rlock == 0) { - SCTP_INP_READ_LOCK(inp); - hold_rlock = 1; - } - } - if (control->end_added) { - out_flags |= MSG_EOR; - if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) - control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; - } - if (control->spec_flags & M_NOTIFICATION) { - out_flags |= MSG_NOTIFICATION; - } - if (uio) - uio->uio_resid -= control->length; - *mp = control->data; - m = control->data; - while (m) { -#ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, - control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); -#endif - sctp_sbfree(control, stcb, &so->so_rcv, m); - freed_so_far += SCTP_BUF_LEN(m); -#ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, - control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); -#endif - m = SCTP_BUF_NEXT(m); - } - control->data = control->tail_mbuf = NULL; - control->length = 0; - if (out_flags & MSG_EOR) { - /* Done with this control */ - goto done_with_control; - } - /* still more to do with this conntrol */ - /* do we really support msg_waitall here? */ - if ((block_allowed == 0) || - ((in_flags & MSG_WAITALL) == 0)) { - goto release; - } - wait_some_more2: - if (so->so_rcv.sb_state & SBS_CANTRCVMORE) - goto release; - if (hold_rlock == 1) { - SCTP_INP_READ_UNLOCK(inp); - hold_rlock = 0; - } - if (hold_sblock == 0) { - SOCKBUF_LOCK(&so->so_rcv); - hold_sblock = 1; - } - if (so->so_rcv.sb_cc <= control->held_length) { - error = sbwait(&so->so_rcv); - if (error) { - goto release; - } - } - if (hold_sblock) { - SOCKBUF_UNLOCK(&so->so_rcv); - hold_sblock = 0; - } - if (control->length == 0) { - /* still nothing here */ - if (control->end_added == 1) { - /* - * he aborted, or is done i.e. - * shutdown - */ - out_flags |= MSG_EOR; - if (control->pdapi_aborted) { - out_flags |= MSG_TRUNC; - if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) - control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; - } - goto done_with_control; - } - if (so->so_rcv.sb_cc > held_length) { - control->held_length = so->so_rcv.sb_cc; - /* - * We don't use held_length while - * getting a message - */ - held_length = 0; - } - goto wait_some_more2; - } - goto get_more_data2; - } else { - /* hard way mbuf by mbuf */ - m = control->data; - if (control->end_added == 0) { - /* need the rlock */ - if (hold_rlock == 0) { - SCTP_INP_READ_LOCK(inp); - hold_rlock = 1; - } - } - if (control->spec_flags & M_NOTIFICATION) { - out_flags |= MSG_NOTIFICATION; + wakeup_read_socket = 0; + if ((control->end_added == 0) || + (TAILQ_NEXT(control, next) == NULL)) { + /* Need to get rlock */ + if (hold_rlock == 0) { + SCTP_INP_READ_LOCK(inp); + hold_rlock = 1; } - while ((m) && (cp_len > 0)) { - if (cp_len >= SCTP_BUF_LEN(m)) { - *mp = m; - atomic_subtract_int(&control->length, SCTP_BUF_LEN(m)); - if (uio) - uio->uio_resid -= SCTP_BUF_LEN(m); - cp_len -= SCTP_BUF_LEN(m); - control->data = SCTP_BUF_NEXT(m); - SCTP_BUF_NEXT(m) = NULL; -#ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, - control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); -#endif - sctp_sbfree(control, stcb, &so->so_rcv, m); - freed_so_far += SCTP_BUF_LEN(m); -#ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, - control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); -#endif - mp = &SCTP_BUF_NEXT(m); - m = control->data; - } else { - /* - * got all he wants and its part of - * this mbuf only. - */ - if (uio) - uio->uio_resid -= SCTP_BUF_LEN(m); - cp_len -= SCTP_BUF_LEN(m); - if (hold_rlock) { - SCTP_INP_READ_UNLOCK(inp); - hold_rlock = 0; - } - if (hold_sblock) { - SOCKBUF_UNLOCK(&so->so_rcv); - hold_sblock = 0; - } - *mp = SCTP_M_COPYM(m, 0, cp_len, - M_TRYWAIT - ); -#ifdef SCTP_LOCK_LOGGING - sctp_log_lock(inp, stcb, SCTP_LOG_LOCK_SOCKBUF_R); -#endif - if (hold_sblock == 0) { - SOCKBUF_LOCK(&so->so_rcv); - hold_sblock = 1; - } - if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) - goto release; - - if (stcb && - stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { - no_rcv_needed = 1; - } - SCTP_BUF_RESV_UF(m, cp_len); - SCTP_BUF_LEN(m) -= cp_len; + } + if (control->end_added) { + out_flags |= MSG_EOR; + if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0)) + control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0; + } + if (control->spec_flags & M_NOTIFICATION) { + out_flags |= MSG_NOTIFICATION; + } + uio->uio_resid = control->length; + *mp = control->data; + m = control->data; + while (m) { #ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, cp_len); + sctp_sblog(&so->so_rcv, + control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m)); #endif - freed_so_far += cp_len; - atomic_subtract_int(&so->so_rcv.sb_cc, cp_len); - if (stcb) { - atomic_subtract_int(&stcb->asoc.sb_cc, cp_len); - if ((freed_so_far >= rwnd_req) && - (control->do_not_ref_stcb == 0) && - (no_rcv_needed == 0)) - sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req); - } + sctp_sbfree(control, stcb, &so->so_rcv, m); + freed_so_far += SCTP_BUF_LEN(m); #ifdef SCTP_SB_LOGGING - sctp_sblog(&so->so_rcv, control->do_not_ref_stcb ? NULL : stcb, - SCTP_LOG_SBRESULT, 0); + sctp_sblog(&so->so_rcv, + control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBRESULT, 0); #endif - goto release; - } - } + m = SCTP_BUF_NEXT(m); + } + control->data = control->tail_mbuf = NULL; + control->length = 0; + if (out_flags & MSG_EOR) { + /* Done with this control */ + goto done_with_control; } } release: @@ -5670,13 +5558,12 @@ sctp_dynamic_set_primary(struct sockaddr *sa, uint32_t vrf_id) int -sctp_soreceive(so, psa, uio, mp0, controlp, flagsp) - struct socket *so; - struct sockaddr **psa; - struct uio *uio; - struct mbuf **mp0; - struct mbuf **controlp; - int *flagsp; +sctp_soreceive(struct socket *so, + struct sockaddr **psa, + struct uio *uio, + struct mbuf **mp0, + struct mbuf **controlp, + int *flagsp) { int error, fromlen; uint8_t sockbuf[256]; @@ -5725,3 +5612,155 @@ sctp_soreceive(so, psa, uio, mp0, controlp, flagsp) } return (error); } + + +int +sctp_l_soreceive(struct socket *so, + struct sockaddr **name, + struct uio *uio, + char **controlp, + int *controllen, + int *flag) +{ + int error, fromlen; + uint8_t sockbuf[256]; + struct sockaddr *from; + struct sctp_extrcvinfo sinfo; + int filling_sinfo = 1; + struct sctp_inpcb *inp; + + inp = (struct sctp_inpcb *)so->so_pcb; + /* pickup the assoc we are reading from */ + if (inp == NULL) { + return (EINVAL); + } + if ((sctp_is_feature_off(inp, + SCTP_PCB_FLAGS_RECVDATAIOEVNT)) || + (controlp == NULL)) { + /* user does not want the sndrcv ctl */ + filling_sinfo = 0; + } + if (name) { + from = (struct sockaddr *)sockbuf; + fromlen = sizeof(sockbuf); + from->sa_len = 0; + } else { + from = NULL; + fromlen = 0; + } + + error = sctp_sorecvmsg(so, uio, + (struct mbuf **)NULL, + from, fromlen, flag, + (struct sctp_sndrcvinfo *)&sinfo, + filling_sinfo); + if ((controlp) && (filling_sinfo)) { + /* + * copy back the sinfo in a CMSG format note that the caller + * has reponsibility for freeing the memory. + */ + if (filling_sinfo) + *controlp = sctp_build_ctl_cchunk(inp, + controllen, + (struct sctp_sndrcvinfo *)&sinfo); + } + if (name) { + /* copy back the address info */ + if (from && from->sa_len) { + *name = sodupsockaddr(from, M_WAIT); + } else { + *name = NULL; + } + } + return (error); +} + + + + + + + +int +sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, int totaddr, int *error) +{ + int added = 0; + int i; + struct sctp_inpcb *inp; + struct sockaddr *sa; + size_t incr = 0; + + sa = addr; + inp = stcb->sctp_ep; + *error = 0; + for (i = 0; i < totaddr; i++) { + if (sa->sa_family == AF_INET) { + incr = sizeof(struct sockaddr_in); + if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + /* assoc gone no un-lock */ + sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_7); + *error = ENOBUFS; + goto out_now; + } + added++; + } else if (sa->sa_family == AF_INET6) { + incr = sizeof(struct sockaddr_in6); + if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + /* assoc gone no un-lock */ + sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_8); + *error = ENOBUFS; + goto out_now; + } + added++; + } + sa = (struct sockaddr *)((caddr_t)sa + incr); + } +out_now: + return (added); +} + +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) +{ + struct sockaddr *sa; + struct sctp_tcb *stcb = NULL; + size_t incr, at, i; + + at = incr = 0; + sa = addr; + *error = *num_v6 = *num_v4 = 0; + /* account and validate addresses */ + for (i = 0; i < *totaddr; i++) { + if (sa->sa_family == AF_INET) { + (*num_v4) += 1; + incr = sizeof(struct sockaddr_in); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + /* Must be non-mapped for connectx */ + *error = EINVAL; + return (NULL); + } + (*num_v6) += 1; + incr = sizeof(struct sockaddr_in6); + } else { + *totaddr = i; + /* we are done */ + break; + } + stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL); + if (stcb != NULL) { + /* Already have or am bring up an association */ + return (stcb); + } + if ((at + incr) > max) { + *totaddr = i; + break; + } + sa = (struct sockaddr *)((caddr_t)sa + incr); + } + return ((struct sctp_tcb *)NULL); +} diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index 3b4acf6..41a5092 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -207,16 +207,23 @@ void sctp_abort_notification(struct sctp_tcb *, int); /* We abort responding to an IP packet for some reason */ void sctp_abort_association(struct sctp_inpcb *, struct sctp_tcb *, - struct mbuf *, int, struct sctphdr *, struct mbuf *); + struct mbuf *, int, struct sctphdr *, struct mbuf *, uint32_t, uint32_t); + /* We choose to abort via user input */ void sctp_abort_an_association(struct sctp_inpcb *, struct sctp_tcb *, int, struct mbuf *); -void +void sctp_handle_ootb(struct mbuf *, int, int, struct sctphdr *, - struct sctp_inpcb *, struct mbuf *); + 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); + +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); int sctp_is_there_an_abort_here(struct mbuf *, int, uint32_t *); uint32_t sctp_is_same_scope(struct sockaddr_in6 *, struct sockaddr_in6 *); @@ -323,6 +330,19 @@ sctp_soreceive(struct socket *so, struct sockaddr **psa, int *flagsp); +/* For those not passing mbufs, this does the + * translations for you. Caller owns memory + * of size controllen returned in controlp. + */ +int +sctp_l_soreceive(struct socket *so, + struct sockaddr **name, + struct uio *uio, + char **controlp, + int *controllen, + int *flag); + + #ifdef SCTP_STAT_LOGGING void sctp_misc_ints(uint8_t from, uint32_t a, uint32_t b, uint32_t c, uint32_t d); diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 07cd299..d9c0890 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -69,7 +69,8 @@ sctp6_input(i_pak, offp, proto) struct sctp_inpcb *in6p = NULL; struct sctp_nets *net; int refcount_up = 0; - uint32_t check, calc_check, vrf_id; + uint32_t check, calc_check; + uint32_t vrf_id = 0, table_id = 0; struct inpcb *in6p_ip; struct sctp_chunkhdr *ch; int length, mlen, offset, iphlen; @@ -77,10 +78,17 @@ sctp6_input(i_pak, offp, proto) struct sctp_tcb *stcb = NULL; int off = *offp; - vrf_id = SCTP_DEFAULT_VRFID; + /* get the VRF and table id's */ + if (SCTP_GET_PKT_VRFID(*i_pak, vrf_id)) { + SCTP_RELEASE_PKT(*i_pak); + return (-1); + } + if (SCTP_GET_PKT_TABLEID(*i_pak, table_id)) { + SCTP_RELEASE_PKT(*i_pak); + return (-1); + } m = SCTP_HEADER_TO_CHAIN(*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)); @@ -104,8 +112,7 @@ sctp6_input(i_pak, offp, proto) #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_INPUT1) { printf("V6 input gets a packet iphlen:%d pktlen:%d\n", iphlen, - SCTP_HEADER_LEN((*i_pak)) - ); + SCTP_HEADER_LEN((*i_pak))); } #endif if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { @@ -181,14 +188,16 @@ sctp_skip_csum: sh->v_tag = init_chk->init.initiate_tag; } if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { - sctp_send_shutdown_complete2(m, iphlen, sh); + sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, + table_id); goto bad; } if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) { goto bad; } if (ch->chunk_type != SCTP_ABORT_ASSOCIATION) - sctp_send_abort(m, iphlen, sh, 0, NULL); + sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, + table_id); goto bad; } else if (stcb == NULL) { refcount_up = 1; @@ -215,11 +224,11 @@ sctp_skip_csum: length = ntohs(ip6->ip6_plen) + iphlen; (void)sctp_common_input_processing(&m, iphlen, offset, length, sh, ch, - in6p, stcb, net, ecn_bits); + in6p, stcb, net, ecn_bits, vrf_id, table_id); /* inp's ref-count reduced && stcb unlocked */ /* XXX this stuff below gets moved to appropriate parts later... */ if (m) - m_freem(m); + sctp_m_freem(m); if ((in6p) && refcount_up) { /* reduce ref-count */ SCTP_INP_WLOCK(in6p); @@ -239,9 +248,10 @@ bad: SCTP_INP_WUNLOCK(in6p); } if (m) - m_freem(m); + sctp_m_freem(m); /* For BSD/MAC this does nothing */ - SCTP_RELEASE_PAK(*i_pak); + SCTP_DETACH_HEADER_FROM_CHAIN(*i_pak); + SCTP_RELEASE_HEADER(*i_pak); return IPPROTO_DONE; } @@ -803,7 +813,6 @@ sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct thread *p); - static int sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct thread *p) @@ -821,10 +830,10 @@ sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { if (control) { - m_freem(control); + SCTP_RELEASE_PKT(control); control = NULL; } - m_freem(m); + SCTP_RELEASE_PKT(m); return EINVAL; } in_inp = (struct inpcb *)inp; @@ -838,9 +847,9 @@ sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, goto connected_type; } if (addr == NULL) { - m_freem(m); + SCTP_RELEASE_PKT(m); if (control) { - m_freem(control); + SCTP_RELEASE_PKT(control); control = NULL; } return (EDESTADDRREQ); @@ -878,7 +887,7 @@ connected_type: if (control) { if (inp->control) { printf("huh? control set?\n"); - m_freem(inp->control); + SCTP_RELEASE_PKT(inp->control); inp->control = NULL; } inp->control = control; |