/* * 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; }
void print_time(short level) { struct timeval now; adl_gettime(&now); event_logii(level, "Time now: %ld sec, %ld usec \n", now.tv_sec, now.tv_usec); }
/** * 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; }
/** * 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; }
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; }
guchar* rbu_scanInitChunkForParameter(guchar * chunk, gushort paramType) { gushort processed_len; guint len = 0, parameterLength = 0; guchar *current_position; guint pad_bytes; SCTP_init *initChunk; SCTP_vlparam_header* vlp; initChunk = (SCTP_init *) chunk; if (initChunk->chunk_header.chunk_id != CHUNK_INIT && initChunk->chunk_header.chunk_id != CHUNK_INIT_ACK) { return FALSE; } len = ntohs(initChunk->chunk_header.chunk_length); current_position = initChunk->variableParams; processed_len = (sizeof(SCTP_chunk_header)+sizeof(SCTP_init_fixed)); while (processed_len < len) { event_logii(INTERNAL_EVENT_0, "rbu_scanInitChunkForParameter : len==%u, processed_len == %u", len, processed_len); vlp = (SCTP_vlparam_header*) current_position; parameterLength = ntohs(vlp->param_length); if (parameterLength < 4 || parameterLength + processed_len > len) return NULL; if (ntohs(vlp->param_type) == paramType) { return current_position; } processed_len += parameterLength; pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; current_position += (parameterLength + pad_bytes * sizeof(unsigned char)); } return NULL; }
/** * Trigger sending of all chunks previously entered with put_Chunk functions * Chunks sent are deleted afterwards. * * FIXME : special treatment for GLOBAL BUFFER, as this is not associated with * any association. * * * @return Errorcode (0 for good case: length bytes sent; 1 or -1 for error) * @param ad_idx pointer to address index or NULL if data is to be sent to default address */ gint bu_sendAllChunks(guint * ad_idx) { gint result, send_len = 0; guchar *send_buffer = NULL; bundling_instance *bu_ptr; gshort idx = 0; bu_ptr = (bundling_instance *) mdi_readBundling(); event_log(INTERNAL_EVENT_0, "bu_sendAllChunks() is being executed..."); if (!bu_ptr) { event_log(VERBOSE, "Sending data from global bundling buffer "); bu_ptr = global_buffer; } if (bu_ptr->locked == TRUE) { bu_ptr->got_send_request = TRUE; if (ad_idx) { bu_ptr->got_send_address = TRUE; bu_ptr->requested_destination = *ad_idx; } event_log(INTERNAL_EVENT_0, "bu_sendAllChunks : sender is LOCKED ---> returning "); return 1; } /* TODO : more intelligent path selection strategy */ /* should take into account PM_INACTIVE */ if (ad_idx != NULL) { if (*ad_idx > 0xFFFF) { error_log(ERROR_FATAL, "address_index too big !"); } else { idx = (short) *ad_idx; } } else { if (bu_ptr->got_send_address) { idx = (short)bu_ptr->requested_destination; } else { idx = -1; /* use last from address */ } } event_logi(VVERBOSE, "bu_sendAllChunks : send to path %d ", idx); if (bu_ptr->sack_in_buffer) send_buffer = bu_ptr->sack_buf; else if (bu_ptr->ctrl_chunk_in_buffer) send_buffer = bu_ptr->ctrl_buf; else if (bu_ptr->data_in_buffer) send_buffer = bu_ptr->data_buf; else { error_log(ERROR_MINOR, "Nothing to send, but bu_sendAllChunks was called !"); return 1; } if (bu_ptr->sack_in_buffer) { rxc_stop_sack_timer(); /* SACKs by default go to the last active address, from which data arrived */ send_len = bu_ptr->sack_position; /* at least sizeof(SCTP_common_header) */ /* at most pointing to the end of SACK chunk */ event_logi(VVERBOSE, "bu_sendAllChunks(sack) : send_len == %d ", send_len); if (bu_ptr->ctrl_chunk_in_buffer) { memcpy(&send_buffer[send_len], &(bu_ptr->ctrl_buf[sizeof(SCTP_common_header)]), (bu_ptr->ctrl_position - sizeof(SCTP_common_header))); send_len += bu_ptr->ctrl_position - sizeof(SCTP_common_header); event_logi(VVERBOSE, "bu_sendAllChunks(sack+ctrl) : send_len == %d ", send_len); } if (bu_ptr->data_in_buffer) { memcpy(&send_buffer[send_len], &(bu_ptr->data_buf[sizeof(SCTP_common_header)]), (bu_ptr->data_position - sizeof(SCTP_common_header))); send_len += bu_ptr->data_position - sizeof(SCTP_common_header); event_logi(VVERBOSE, "bu_sendAllChunks(sack+data) : send_len == %d ", send_len); } } else if (bu_ptr->ctrl_chunk_in_buffer) { send_len = bu_ptr->ctrl_position; event_logi(VVERBOSE, "bu_sendAllChunks(ctrl) : send_len == %d ", send_len); if (bu_ptr->data_in_buffer) { memcpy(&send_buffer[send_len], &(bu_ptr->data_buf[sizeof(SCTP_common_header)]), (bu_ptr->data_position - sizeof(SCTP_common_header))); send_len += bu_ptr->data_position - sizeof(SCTP_common_header); event_logi(VVERBOSE, "bu_sendAllChunks(ctrl+data) : send_len == %d ", send_len); } } else if (bu_ptr->data_in_buffer) send_len = bu_ptr->data_position; event_logi(VVERBOSE, "bu_sendAllChunks(finally) : send_len == %d ", send_len); if (send_len > 1480) { fprintf(stderr, "MTU definitely exceeded (%u) - aborting\n",send_len); fprintf(stderr, "sack_position: %u, ctrl_position: %u, data_position: %u\n", bu_ptr->sack_position,bu_ptr->ctrl_position,bu_ptr->data_position); abort(); } if ((bu_ptr->data_in_buffer) && (idx != -1)) pm_chunksSentOn(idx); event_logii(VERBOSE, "bu_sendAllChunks() : sending message len==%u to adress idx=%d", send_len, idx); result = mdi_send_message((SCTP_message *) send_buffer, send_len, idx); event_logi(VVERBOSE, "bu_sendAllChunks(): result == %s ", (result==0)?"OKAY":"ERROR"); /* reset all positions */ bu_ptr->sack_in_buffer = FALSE; bu_ptr->ctrl_chunk_in_buffer = FALSE; bu_ptr->data_in_buffer = FALSE; bu_ptr->got_send_request = FALSE; bu_ptr->got_send_address = FALSE; bu_ptr->data_position = sizeof(SCTP_common_header); bu_ptr->ctrl_position = sizeof(SCTP_common_header); bu_ptr->sack_position = sizeof(SCTP_common_header); return result; }
/* * rbu_findAddress: looks for address type parameters in INIT or INIT-ACKs * All parameters within the chunk are looked at, and the n-th supported address is * copied into the provided buffer pointed to by the foundAddress parameter. * If there are less than n addresses, an appropriate error is * returned. n should be at least 1, of course. * @param chunk pointer to an INIT or INIT ACK chunk * @param n get the n-th address * @param foundAddress pointer to a buffer where an address, if found, will be copied * @return -1 for parameter problem, 0 for success (i.e. address found), 1 if there are not * that many addresses in the chunk. */ gint rbu_findAddress(guchar * chunk, guint n, union sockunion* foundAddress, int supportedAddressTypes) { gushort processed_len; guint len = 0, parameterLength = 0; guchar *current_position; guint pad_bytes; SCTP_init *initChunk; SCTP_vlparam_header* vlp; SCTP_ip_address * address; unsigned int foundAddressNumber = 0; initChunk = (SCTP_init *) chunk; if (foundAddress == NULL || n < 1 || n > SCTP_MAX_NUM_ADDRESSES) return -1; if (initChunk->chunk_header.chunk_id != CHUNK_INIT && initChunk->chunk_header.chunk_id != CHUNK_INIT_ACK) { return -1; } len = ntohs(initChunk->chunk_header.chunk_length); current_position = initChunk->variableParams; processed_len = (sizeof(SCTP_chunk_header)+sizeof(SCTP_init_fixed)); while (processed_len < len) { event_logii(INTERNAL_EVENT_0, "rbu_findAddress : len==%u, processed_len == %u", len, processed_len); vlp = (SCTP_vlparam_header*) current_position; parameterLength = ntohs(vlp->param_length); if (parameterLength < 4 || parameterLength + processed_len > len) return -1; if (ntohs(vlp->param_type) == VLPARAM_IPV4_ADDRESS && supportedAddressTypes & SUPPORT_ADDRESS_TYPE_IPV4) { /* discard invalid addresses */ foundAddressNumber++; if (foundAddressNumber == n) { address = (SCTP_ip_address *)current_position; /* copy the address over to the user buffer */ foundAddress->sa.sa_family = AF_INET; foundAddress->sin.sin_port = 0; foundAddress->sin.sin_addr.s_addr = address->dest_addr.sctp_ipv4; return 0; } #ifdef HAVE_IPV6 } else if (ntohs(vlp->param_type) == VLPARAM_IPV6_ADDRESS && supportedAddressTypes & SUPPORT_ADDRESS_TYPE_IPV6) { /* discard invalid addresses */ foundAddressNumber++; if (foundAddressNumber == n) { address = (SCTP_ip_address *)current_position; /* copy the address over to the user buffer */ foundAddress->sa.sa_family = AF_INET6; foundAddress->sin6.sin6_port = htons(0); foundAddress->sin6.sin6_flowinfo = htonl(0); #ifdef HAVE_SIN6_SCOPE_ID foundAddress->sin6.sin6_scope_id = htonl(0); #endif memcpy(foundAddress->sin6.sin6_addr.s6_addr, address->dest_addr.sctp_ipv6, sizeof(struct in6_addr)); return 0; } #endif } processed_len += parameterLength; pad_bytes = ((processed_len % 4) == 0) ? 0 : (4 - processed_len % 4); processed_len += pad_bytes; current_position += (parameterLength + pad_bytes * sizeof(unsigned char)); } return 1; }