/* * $Id: sbundling.c,v 1.6 2001/04/05 09:17:37 ajung Exp $ * * SCTP implementation according to RFC 2960. * Copyright (C) 2000 by Siemens AG, Munich, Germany. * * Realized in co-operation between Siemens AG * and University of Essen, Institute of Computer Networking Technology. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * There are two mailinglists available at http://www.sctp.de which should be * used for any discussion related to this implementation. * * Contact: discussion AT sctp.de * Michael.Tuexen AT icn.siemens.de * ajung AT exp-math.uni-essen.de * * Purpose: This module bundels chunks chunks to be send into UDP-datagramms. Chunks are accepted * with the putChunk function until sendChunk is called, which causes the transmission of * all chunks accumulated so far. * In the receive direction, this module gets UDP-dataparts from SCTP message-distribution, * which are disassambled (debundled) into chunks. Depending on the chunk-type, the chunk * are distributed to SCTP-control, RX-control, pathmanagement or reliable transfer. */ #include <stdio.h> #include <stdlib.h> #include "bundling.h" #include "messages.h" #include "dll_main.h" #include "distribution.h" #include "recvctrl.h" #include "reltransfer.h" #include "errorhandler.h" #define TOTAL_SIZE(buf) ((buf)->ctrl_position+(buf)->sack_position+(buf)->data_position- 2*sizeof(SCTP_common_header)) /** * this struct contains all data belonging to a bundling module */ typedef struct bundling_instance_struct { //@{ /// buffer for control chunks guchar ctrl_buf[MAX_MTU_SIZE]; /// buffer for sack chunks guchar sack_buf[MAX_MTU_SIZE]; /// buffer for data chunks guchar data_buf[MAX_MTU_SIZE]; /* Leave some space for the SCTP common header */ /// current position in the buffer for control chunks guint ctrl_position; /// current position in the buffer for sack chunks guint sack_position; /// current position in the buffer for data chunks guint data_position; /// is there data to be sent in the buffer ? gboolean data_in_buffer; /// is there a control chunk to be sent in the buffer ? gboolean ctrl_chunk_in_buffer; /// is there a sack chunk to be sent in the buffer ? gboolean sack_in_buffer; /// gboolean got_send_request; /// gboolean got_send_address; /// gboolean locked; /// guint requested_destination; //@} } bundling_instance; /** * one static variable for a buffer that is used, if no bundling instance has been * allocated and initialized yet */ static bundling_instance *global_buffer; void bu_init_bundling(void) { global_buffer = bu_new(); } /** * Creates a new bundling instance and returns a pointer to its data. * @return pointer to an instance of the bundling data */ gpointer bu_new(void) { /* Alloc new bundling_instance data struct */ bundling_instance *ptr; ptr = malloc(sizeof(bundling_instance)); if (!ptr) { error_log(ERROR_MAJOR, "Malloc failed"); return 0; } ptr->ctrl_position = sizeof(SCTP_common_header); /* start adding data after that header ! */ ptr->data_position = sizeof(SCTP_common_header); /* start adding data after that header ! */ ptr->sack_position = sizeof(SCTP_common_header); /* start adding data after that header ! */ ptr->data_in_buffer = FALSE; ptr->ctrl_chunk_in_buffer = FALSE; ptr->sack_in_buffer = FALSE; ptr->got_send_request = FALSE; ptr->got_send_address = FALSE; ptr->locked = FALSE; ptr->requested_destination = 0; return ptr; } /** * Deletes a bundling instance * * @param Pointer which was returned by bu_new() */ void bu_delete(gpointer buPtr) { event_log(INTERNAL_EVENT_0, "deleting bundling"); free(buPtr); } /** * Keep sender from sending data right away - wait after received chunks have * been diassembled completely. */ void bu_lock_sender() { bundling_instance *bu_ptr; event_log(VERBOSE, "bu_lock_sender() 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, "Setting global bundling buffer "); bu_ptr = global_buffer; } bu_ptr->locked = TRUE; bu_ptr->got_send_request = FALSE; } /** * Enable sending again - wait after received chunks have * been diassembled completely. */ void bu_unlock_sender(guint* ad_idx) { bundling_instance *bu_ptr; event_log(VERBOSE, "bu_unlock_sender() 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, "Setting global bundling buffer "); bu_ptr = global_buffer; } bu_ptr->locked = FALSE; if (bu_ptr->got_send_request == TRUE) bu_sendAllChunks(ad_idx); } /** * 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) { 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 (TOTAL_SIZE(bu_ptr) + CHUNKP_LENGTH((SCTP_chunk_header *) chunk) >= MAX_SCTP_PDU) { lock = bu_ptr->locked; error_log(ERROR_MAJOR, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to default address !"); if (lock) bu_ptr->locked = FALSE; result = bu_sendAllChunks(NULL); if (lock) bu_ptr->locked = TRUE; } 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((SCTP_chunk_header *) chunk)); event_logii(VERBOSE, "Put SACK Chunk Length : %u , Total buffer size : %u\n", CHUNKP_LENGTH((SCTP_chunk_header *) chunk), TOTAL_SIZE(bu_ptr)); /* SACK always multiple of 32 bytes, do not care about padding */ bu_ptr->sack_position += CHUNKP_LENGTH((SCTP_chunk_header *) chunk); bu_ptr->sack_in_buffer = TRUE; return 0; } /** * 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) { 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((SCTP_chunk_header *) chunk) >= MAX_SCTP_PDU) { lock = bu_ptr->locked; error_log(ERROR_MAJOR, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to default address !"); if (lock) bu_ptr->locked = FALSE; result = bu_sendAllChunks(NULL); if (lock) bu_ptr->locked = TRUE; } memcpy(&(bu_ptr->ctrl_buf[bu_ptr->ctrl_position]), chunk, CHUNKP_LENGTH((SCTP_chunk_header *) chunk)); event_logii(VERBOSE, "Put Control Chunk Length : %u , Total buffer size : %u\n", CHUNKP_LENGTH((SCTP_chunk_header *) chunk), TOTAL_SIZE(bu_ptr)); bu_ptr->ctrl_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->ctrl_buf[bu_ptr->ctrl_position] = 0; bu_ptr->ctrl_position++; } } bu_ptr->ctrl_chunk_in_buffer = TRUE; return 0; } /** * 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) { gint result; 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; error_log(ERROR_MAJOR, "Chunk Length exceeded MAX_SCTP_PDU : sending chunk to default address !"); if (lock) bu_ptr->locked = FALSE; result = bu_sendAllChunks(NULL); if (lock) bu_ptr->locked = TRUE; } 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); event_logii(VERBOSE, "Put Data Chunk Length : %u , Total buffer size : %u\n", CHUNKP_LENGTH((SCTP_chunk_header *) chunk), TOTAL_SIZE(bu_ptr)); /* 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_log(VERBOSE, "bu_put_Data_Chunk() : Added Padding bytes"); bu_ptr->data_in_buffer = TRUE; return 0; } /** * Trigger sending of all chunks previously entered with put_Chunk functions * Chunks sent are deleted afterwards. * * @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) { 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) { 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 = pm_readPrimaryPath(); if (pm_readPrimaryPath() == -1) { error_log(ERROR_MINOR, "Primary Path returned was -1 in bu_sendAllChunks(), using PATH 0"); idx = 0; } } } 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 */ idx = rxc_read_last_active_address(); send_len = bu_ptr->sack_position; /* at least sizeof(SCTP_common_header) */ /* at most pointing to the end of SACK chunk */ 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); } 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); } } else if (bu_ptr->ctrl_chunk_in_buffer) { send_len = bu_ptr->ctrl_position; 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); } } else if (bu_ptr->data_in_buffer) send_len = bu_ptr->data_position; if (bu_ptr->data_in_buffer) pm_chunksSentOn(idx); event_logii(VERBOSE, "bu_sendAllChunks() : sending message len==%u to adress idx=%u", send_len, idx); result = mdi_send_message((SCTP_message *) send_buffer, send_len, idx); event_logi(VVERBOSE, "bu_sendAllChunks(): result==%d ", result); /* 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; }
Archive powered by MHonArc 2.6.19+.