/* * rbu_findChunk: looks for chunk_type in a newly received datagram * * All chunks within the datagram are looked at, until one is found * that equals the parameter chunk_type. * @param datagram pointer to the newly received data * @param len stop after this many bytes * @param chunk_type chunk type to look for * @return pointer to first chunk of chunk_type in SCTP datagram, else NULL */ guchar* rbu_findChunk(guchar * datagram, guint len, gushort chunk_type) { gushort processed_len = 0, chunk_len = 0; guchar *current_position; guint pad_bytes; SCTP_simple_chunk *chunk; current_position = datagram; /* points to the first chunk in this pdu */ while (processed_len < len) { event_logii(INTERNAL_EVENT_0, "rbu_findChunk : len==%u, processed_len == %u", len, processed_len); chunk = (SCTP_simple_chunk *) current_position; if (chunk->chunk_header.chunk_id == chunk_type) return current_position; else { chunk_len = CHUNKP_LENGTH((SCTP_chunk_header *) chunk); if (chunk_len < 4 || chunk_len + processed_len > len) return NULL; processed_len += CHUNKP_LENGTH((SCTP_chunk_header *) chunk); pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; chunk_len = (CHUNKP_LENGTH((SCTP_chunk_header *) chunk) + pad_bytes * sizeof(unsigned char)); if (chunk_len < 4 || chunk_len + processed_len > len) return NULL; current_position += chunk_len; } } return NULL; }
/** * Called by recvcontrol, when a SACK must be piggy-backed * TODO : Handle multiple calls to this function between two send events * * @param chunk pointer to chunk, that is to be put in the bundling buffer * @return error value, 0 on success, -1 on error */ gint bu_put_SACK_Chunk (SCTP_sack_chunk * chunk, unsigned int * dest_index) { gint result; bundling_instance *bu_ptr; gboolean lock; event_log(INTERNAL_EVENT_0, "bu_put_SACK_Chunk() was called "); bu_ptr = (bundling_instance *) mdi_readBundling (); if (!bu_ptr) { /* Assume that no association exists, so we take the global bundling buffer */ event_log(VERBOSE, "Copying SACK to global bundling buffer "); bu_ptr = global_buffer; } if (SACK_SIZE(bu_ptr) + CHUNKP_LENGTH((chunk_fixed_t * ) chunk) >= MAX_SCTP_PDU) { lock = bu_ptr->locked; event_logi( VERBOSE, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to address %u !", (dest_index==NULL)?0:*dest_index); if (lock) bu_ptr->locked = FALSE; result = bu_sendAllChunks (dest_index); if (lock) bu_ptr->locked = TRUE; } else if (dest_index != NULL) { bu_ptr->got_send_address = TRUE; bu_ptr->requested_destination = *dest_index; } if (bu_ptr->sack_in_buffer == TRUE) { /* multiple calls in between */ event_log( INTERNAL_EVENT_0, "bu_put_SACK_Chunk was called a second time, deleting first chunk"); bu_ptr->sack_position = sizeof(SCTP_common_header); } memcpy (&(bu_ptr->sack_buf[bu_ptr->sack_position]), chunk, CHUNKP_LENGTH((chunk_fixed_t * ) chunk)); bu_ptr->sack_position += CHUNKP_LENGTH((chunk_fixed_t * ) chunk); bu_ptr->sack_in_buffer = TRUE; event_logii(VERBOSE, "Put SACK Chunk Length : %u , Total buffer size now: %u\n", CHUNKP_LENGTH((chunk_fixed_t *) chunk), TOTAL_SIZE(bu_ptr)); /* SACK always multiple of 32 bytes, do not care about padding */ return 0; }
/** * looks for Error chunk_type in a newly received datagram * that contains a special error cause code * * All chunks within the datagram are lookes at, until one is found * that equals the parameter chunk_type. * @param datagram pointer to the newly received data * @param len stop after this many bytes * @param error_cause error cause code to look for * @return true is chunk_type exists in SCTP datagram, false if it is not in there */ gboolean rbu_scanDatagramForError(guchar * datagram, guint len, gushort error_cause) { gushort processed_len = 0, param_length = 0, chunk_length = 0; gushort err_len = 0; guchar *current_position; guint pad_bytes; SCTP_simple_chunk *chunk; SCTP_staleCookieError *err_chunk; current_position = datagram; /* points to the first chunk in this pdu */ while (processed_len < len) { event_logii(VERBOSE, "rbu_scanDatagramForError : len==%u, processed_len == %u", len, processed_len); chunk = (SCTP_simple_chunk *) current_position; chunk_length = CHUNKP_LENGTH((SCTP_chunk_header *) chunk); if (chunk_length < 4 || chunk_length + processed_len > len) return FALSE; if (chunk->chunk_header.chunk_id == CHUNK_ERROR) { if (chunk_length < 4 || chunk_length + processed_len > len) return FALSE; event_log(INTERNAL_EVENT_0, "rbu_scanDatagramForError : Error Chunk Found"); /* now search for error parameter that fits */ while (err_len < chunk_length - sizeof(SCTP_chunk_header)) { err_chunk = (SCTP_staleCookieError *) &(chunk->simple_chunk_data[err_len]); if (ntohs(err_chunk->vlparam_header.param_type) == error_cause) { event_logi(VERBOSE, "rbu_scanDatagramForError : Error Cause %u found -> Returning TRUE", error_cause); return TRUE; } param_length = ntohs(err_chunk->vlparam_header.param_length); if (param_length < 4 || param_length + err_len > len) return FALSE; err_len += param_length; while ((err_len % 4) != 0) err_len++; } } processed_len += chunk_length; pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; chunk_length = (CHUNKP_LENGTH((SCTP_chunk_header *) chunk) + pad_bytes * sizeof(unsigned char)); if (chunk_length < 4 || chunk_length + processed_len > len) return FALSE; current_position += chunk_length; } event_logi(VERBOSE, "rbu_scanDatagramForError : Error Cause %u NOT found -> Returning FALSE", error_cause); return FALSE; }
/** * this function used for bundling of control chunks * Used by SCTP-control and Path management * * @param chunk pointer to chunk, that is to be put in the bundling buffer * @return TODO : error value, 0 on success */ gint bu_put_Ctrl_Chunk (SCTP_simple_chunk * chunk, unsigned int * dest_index) { gint result; bundling_instance *bu_ptr; gint count; gboolean lock; event_log(INTERNAL_EVENT_0, "bu_put_Ctrl_Chunk() was called"); bu_ptr = (bundling_instance *) mdi_readBundling (); if (!bu_ptr) { /* Assume that no association exists, so we take the global bundling buffer */ event_log(VERBOSE, "Copying Control Chunk to global bundling buffer "); bu_ptr = global_buffer; } if (TOTAL_SIZE(bu_ptr) + CHUNKP_LENGTH((chunk_fixed_t * ) chunk) >= MAX_SCTP_PDU) { lock = bu_ptr->locked; event_logi( VERBOSE, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to address %u !", (dest_index==NULL)?0:*dest_index); if (lock) bu_ptr->locked = FALSE; result = bu_sendAllChunks (dest_index); if (lock) bu_ptr->locked = TRUE; } else if (dest_index != NULL) { bu_ptr->got_send_address = TRUE; bu_ptr->requested_destination = *dest_index; } memcpy (&(bu_ptr->ctrl_buf[bu_ptr->ctrl_position]), chunk, CHUNKP_LENGTH((chunk_fixed_t * ) chunk)); bu_ptr->ctrl_position += CHUNKP_LENGTH((chunk_fixed_t * ) chunk); /* insert padding, if necessary */ if ((CHUNKP_LENGTH((chunk_fixed_t *) chunk) % 4) != 0) { for (count = 0; count < (4 - (CHUNKP_LENGTH((chunk_fixed_t *) chunk) % 4)); count++) { bu_ptr->ctrl_buf[bu_ptr->ctrl_position] = 0; bu_ptr->ctrl_position++; } } event_logii( VERBOSE, "Put Control Chunk Length : %u , Total buffer size now (includes pad): %u\n", CHUNKP_LENGTH((chunk_fixed_t *) chunk), TOTAL_SIZE(bu_ptr)); bu_ptr->ctrl_chunk_in_buffer = TRUE; return 0; }
unsigned int rbu_scanPDU(guchar * pdu, guint len) { gushort processed_len = 0; gushort chunk_len = 0; unsigned int result = 0; guchar *current_position; guint pad_bytes; SCTP_simple_chunk *chunk; current_position = pdu; /* points to the first chunk in this pdu */ while (processed_len < len) { event_logii(VERBOSE, "rbu_scanPDU : len==%u, processed_len == %u", len, processed_len); chunk = (SCTP_simple_chunk *) current_position; chunk_len = CHUNKP_LENGTH((SCTP_chunk_header *) chunk); if (chunk_len < 4 || chunk_len + processed_len > len) return result; if (chunk->chunk_header.chunk_id <= 30) { result = result | (1 << chunk->chunk_header.chunk_id); event_logii(VERBOSE, "rbu_scanPDU : Chunk type==%u, result == %x", chunk->chunk_header.chunk_id, result); } else { result = result | (1 << 31); event_logii(VERBOSE, "rbu_scanPDU : Chunk type==%u setting bit 31 --> result == %x", chunk->chunk_header.chunk_id, result); } processed_len += chunk_len; pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; chunk_len = (CHUNKP_LENGTH((SCTP_chunk_header *) chunk) + pad_bytes * sizeof(unsigned char)); if (chunk_len < 4 || chunk_len + processed_len > len) return result; current_position += chunk_len; } return result; }
/** * this function used for putting data chunks into the buffer * Used only in the flow control module * * @param chunk pointer to chunk, that is to be put in the bundling buffer * @return TODO : error value, 0 on success */ gint bu_put_Data_Chunk(SCTP_simple_chunk * chunk,unsigned int * dest_index) { bundling_instance *bu_ptr; gint count; gboolean lock; event_log(INTERNAL_EVENT_0, "bu_put_Data_Chunk() was called "); bu_ptr = (bundling_instance *) mdi_readBundling(); if (!bu_ptr) { /* Assume that no association exists, so we take the global bundling buffer */ event_log(VERBOSE, "Copying data to global bundling buffer "); bu_ptr = global_buffer; } if (TOTAL_SIZE(bu_ptr) + CHUNKP_LENGTH((SCTP_chunk_header *) chunk) >= MAX_SCTP_PDU) { lock = bu_ptr->locked; event_logi(VERBOSE, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to address %u !", (dest_index==NULL)?0:*dest_index); if (lock) bu_ptr->locked = FALSE; bu_sendAllChunks(dest_index); if (lock) bu_ptr->locked = TRUE; } else if (dest_index != NULL) { bu_ptr->got_send_address = TRUE; bu_ptr->requested_destination = *dest_index; } memcpy(&(bu_ptr->data_buf[bu_ptr->data_position]), chunk, CHUNKP_LENGTH((SCTP_chunk_header *) chunk)); bu_ptr->data_position += CHUNKP_LENGTH((SCTP_chunk_header *) chunk); /* insert padding, if necessary */ if ((CHUNKP_LENGTH((SCTP_chunk_header *) chunk) % 4) != 0) { for (count = 0; count < (4 - (CHUNKP_LENGTH((SCTP_chunk_header *) chunk) % 4)); count++) { bu_ptr->data_buf[bu_ptr->data_position] = 0; bu_ptr->data_position++; } } event_logii(VERBOSE, "Put Data Chunk Length : %u , Total buffer size (incl. padding): %u\n", CHUNKP_LENGTH((SCTP_chunk_header *) chunk), TOTAL_SIZE(bu_ptr)); bu_ptr->data_in_buffer = TRUE; /* if SACK is waiting, force sending it along */ if (rxc_sack_timer_is_running() == TRUE) rxc_create_sack(dest_index, TRUE); return 0; }
/** * Disassembles chunks from a received datagram * * FIXME : data chunks may only be parsed after control chunks..... * * All chunks within the datagram are dispatched and sent to the appropriate * module, i.e.: control chunks are sent to sctp_control/pathmanagement, * SACK chunks to reliable_transfer, and data_chunks to RX_control. * Those modules must get a pointer to the start of a chunk and * information about its size (without padding). * @param address_index index of address on which this data arrived * @param datagram pointer to first chunk of the newly received data * @param len length of payload (i.e. len of the concatenation of chunks) */ gint rbu_rcvDatagram(guint address_index, guchar * datagram, guint len) { /* sctp common header header has been verified */ /* tag (if association is established) and CRC is okay */ /* get first chunk-id and length, pass pointers & len on to relevant module : - CHUNK_INIT, CHUNK_INIT_ACK,CHUNK_ABORT, CHUNK_SHUTDOWN,CHUNK_SHUTDOWN_ACK CHUNK_COOKIE_ECHO,CHUNK_COOKIE_ACK go to SCTP_CONTROL (change of association state) - CHUNK_HBREQ, CHUNK_HBACK go to PATH_MAN instance - CHUNK_SACK goes to RELIABLE_TRANSFER - CHUNK_ERROR probably to SCTP_CONTROL as well (at least there !) - CHUNK_DATA goes to RX_CONTROL */ guchar *current_position; gushort processed_len = 0, chunk_len; gushort pad_bytes; SCTP_simple_chunk *chunk; gboolean data_chunk_received = FALSE; int association_state = STATE_OK; gboolean send_it = FALSE; bu_lock_sender(); current_position = datagram; /* points to the first chunk in this pdu */ event_log(INTERNAL_EVENT_0, "Entered rbu_rcvDatagram()...... "); /* CHECKME : beim Empfangen leerer Chunks tritt im Bundling eine Endlosschleife auf ??? */ while (processed_len < len) { chunk = (SCTP_simple_chunk *) current_position; chunk_len = CHUNKP_LENGTH((SCTP_chunk_header *) chunk); event_logiiii(INTERNAL_EVENT_0, "rbu_rcvDatagram(address=%u) : len==%u, processed_len = %u, chunk_len=%u", address_index, len, processed_len, chunk_len); if ((processed_len+chunk_len) > len || chunk_len < 4) { error_logiii(ERROR_MINOR, "Faulty chunklen=%u, total len=%u,processed_len=%u --> dropping packet !", chunk_len,len,processed_len); /* if the association has already been removed, we cannot unlock it anymore */ bu_unlock_sender(&address_index); return 1; } /* * TODO : Add return values to the chunk-functions, where they can indicate what * to do with the rest of the datagram (i.e. DISCARD after stale COOKIE_ECHO * with tie tags that do not match the current ones) */ switch (chunk->chunk_header.chunk_id) { case CHUNK_DATA: event_log(INTERNAL_EVENT_0, "******************* Bundling received DATA chunk"); rxc_data_chunk_rx((SCTP_data_chunk*) chunk, address_index); data_chunk_received = TRUE; break; case CHUNK_INIT: event_log(INTERNAL_EVENT_0, "******************* Bundling received INIT chunk"); association_state = sctlr_init((SCTP_init *) chunk); break; case CHUNK_INIT_ACK: event_log(INTERNAL_EVENT_0, "******************* Bundling received INIT ACK chunk"); association_state = sctlr_initAck((SCTP_init *) chunk); break; case CHUNK_SACK: event_log(INTERNAL_EVENT_0, "******************* Bundling received SACK chunk"); rtx_process_sack(address_index, chunk, len); break; case CHUNK_HBREQ: event_log(INTERNAL_EVENT_0, "******************* Bundling received HB_REQ chunk"); pm_heartbeat((SCTP_heartbeat *) chunk, address_index); break; case CHUNK_HBACK: event_log(INTERNAL_EVENT_0, "******************* Bundling received HB_ACK chunk"); pm_heartbeatAck((SCTP_heartbeat *) chunk); break; case CHUNK_ABORT: event_log(INTERNAL_EVENT_0, "******************* Bundling received ABORT chunk"); association_state = sctlr_abort(); break; case CHUNK_SHUTDOWN: event_log(INTERNAL_EVENT_0, "******************* Bundling received SHUTDOWN chunk"); association_state = sctlr_shutdown((SCTP_simple_chunk *) chunk); break; case CHUNK_SHUTDOWN_ACK: event_log(INTERNAL_EVENT_0, "******************* Bundling received SHUTDOWN ACK chunk"); association_state = sctlr_shutdownAck(); break; case CHUNK_ERROR: event_log(INTERNAL_EVENT_0, "Error Chunk"); eh_recv_chunk(chunk); break; case CHUNK_COOKIE_ECHO: event_log(INTERNAL_EVENT_0, "******************* Bundling received COOKIE ECHO chunk"); sctlr_cookie_echo((SCTP_cookie_echo *) chunk); break; case CHUNK_COOKIE_ACK: event_log(INTERNAL_EVENT_0, "******************* Bundling received COOKIE ACK chunk"); sctlr_cookieAck((SCTP_simple_chunk *) chunk); break; /* case CHUNK_ECNE: case CHUNK_CWR: event_logi(INTERNAL_EVENT_0, "Chunktype %u not Supported Yet !!!!!!!!", chunk->chunk_header.chunk_id); break;*/ case CHUNK_SHUTDOWN_COMPLETE: event_log(INTERNAL_EVENT_0, "******************* Bundling received SHUTDOWN_COMPLETE chunk"); association_state = sctlr_shutdownComplete(); break; case CHUNK_FORWARD_TSN: if (mdi_supportsPRSCTP() == TRUE) { event_log(INTERNAL_EVENT_0, "******************* Bundling received FORWARD_TSN chunk"); rxc_process_forward_tsn((SCTP_simple_chunk *) chunk); break; } else continue; /* case CHUNK_ASCONF: */ /* check that ASCONF chunks are standalone chunks, not bundled with any other chunks. Else ignore the ASCONF chunk (but not the others) */ /* event_log(INTERNAL_EVENT_0, "Bundling received ASCONF chunk"); asc_recv_asconf_chunk((SCTP_simple_chunk *) chunk); break; case CHUNK_ASCONF_ACK: event_log(INTERNAL_EVENT_0, "Bundling received ASCONF_ACK chunk"); asc_recv_asconf_ack((SCTP_simple_chunk *) chunk); break; */ default: /* 00 - Stop processing this SCTP packet and discard it, do not process any further chunks within it. 01 - Stop processing this SCTP packet and discard it, do not process any further chunks within it, and report the unrecognized parameter in an 'Unrecognized Parameter Type' (in either an ERROR or in the INIT ACK). 10 - Skip this chunk and continue processing. 11 - Skip this chunk and continue processing, but report in an ERROR Chunk using the 'Unrecognized Chunk Type' cause of error. */ if ((chunk->chunk_header.chunk_id & 0xC0) == 0x0) { /* 00 */ processed_len = len; event_logi(EXTERNAL_EVENT_X, "00: Unknown chunktype %u in rbundling.c", chunk->chunk_header.chunk_id); } else if ((chunk->chunk_header.chunk_id & 0xC0) == 0x40) { /* 01 */ processed_len = len; eh_send_unrecognized_chunktype((unsigned char*)chunk,chunk_len); event_logi(EXTERNAL_EVENT_X, "01: Unknown chunktype %u in rbundling.c",chunk->chunk_header.chunk_id); } else if ((chunk->chunk_header.chunk_id & 0xC0) == 0x80) { /* 10 */ /* nothing */ event_logi(EXTERNAL_EVENT_X, "10: Unknown chunktype %u in rbundling.c",chunk->chunk_header.chunk_id); } else if ((chunk->chunk_header.chunk_id & 0xC0) == 0xC0) { /* 11 */ event_logi(EXTERNAL_EVENT_X, "11: Unknown chunktype %u in rbundling.c", chunk->chunk_header.chunk_id); eh_send_unrecognized_chunktype((unsigned char*)chunk,chunk_len); } break; } processed_len += chunk_len; pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; current_position += (chunk_len + pad_bytes) * sizeof(unsigned char); if (association_state != STATE_OK) processed_len = len; event_logiiii(VVERBOSE, "processed_len=%u, pad_bytes=%u, current_position=%u, chunk_len=%u", processed_len, pad_bytes, current_position,chunk_len); } if (association_state != STATE_STOP_PARSING_REMOVED) { if (data_chunk_received == TRUE) { /* update SACK structure and start SACK timer */ rxc_all_chunks_processed(TRUE); } else { /* update SACK structure and datagram counter */ rxc_all_chunks_processed(FALSE); } /* optionally also add a SACK chunk, at least for every second datagram * see section 6.2, second paragraph */ if (data_chunk_received == TRUE){ send_it = rxc_create_sack(&address_index, FALSE); se_doNotifications(); if (send_it==TRUE) bu_sendAllChunks(&address_index); } /* if the association has already been removed, we cannot unlock it anymore */ bu_unlock_sender(&address_index); } return 0; }