int bundle_add_block(struct mmem *bundlemem, uint8_t type, uint8_t flags, uint8_t *data, int d_len) { struct bundle_t *bundle; struct bundle_block_t *block; uint8_t i; int n; n = mmem_realloc(bundlemem, bundlemem->size + d_len + sizeof(struct bundle_block_t)); if( !n ) { return -1; } bundle = (struct bundle_t *) MMEM_PTR(bundlemem); /* FIXME: Make sure we don't traverse outside of our allocated memory */ /* Go through the blocks until we're behind the last one */ block = (struct bundle_block_t *) bundle->block_data; for (i=0;i<bundle->num_blocks;i++) { /* None of these is the last block anymore */ block->flags &= ~BUNDLE_BLOCK_FLAG_LAST; block = (struct bundle_block_t *) &block->payload[block->block_size]; } block->type = type; block->flags = BUNDLE_BLOCK_FLAG_LAST | flags; block->block_size = d_len; bundle->num_blocks++; memcpy(block->payload, data, d_len); return d_len; }
static size_t bundle_decode_block(struct mmem* const bundlemem, const uint8_t* const buffer, const size_t max_len) { uint8_t type; int block_offs = 0; size_t offs = 0; uint32_t flags, size; struct bundle_t *bundle; struct bundle_block_t *block; int n; type = buffer[offs]; offs++; /* Flags */ offs += sdnv_decode(&buffer[offs], max_len-offs, &flags); /* Payload Size */ offs += sdnv_decode(&buffer[offs], max_len-offs, &size); if (size > max_len-offs) { LOG(LOGD_DTN, LOG_BUNDLE, LOGL_ERR, "Bundle payload length too big: %lu > %lu", size, max_len-offs); return 0; } block_offs = bundlemem->size; if( type == BUNDLE_BLOCK_TYPE_AEB ) { // TODO remove const cast return offs + bundle_ageing_parse_age_extension_block(bundlemem, type, flags, (uint8_t*)&buffer[offs], size); } n = mmem_realloc(bundlemem, bundlemem->size + sizeof(struct bundle_block_t) + size); if( !n ) { LOG(LOGD_DTN, LOG_BUNDLE, LOGL_ERR, "Bundle payload length too big for MMEM."); return 0; } bundle = (struct bundle_t *) MMEM_PTR(bundlemem); bundle->num_blocks++; /* Add the block to the end of the bundle */ block = (struct bundle_block_t *)((uint8_t *)bundle + block_offs); block->type = type; block->flags = flags; block->block_size = size; /* Copy the actual payload over */ memcpy(block->payload, &buffer[offs], block->block_size); return offs + block->block_size; }
/** * Return values: * 1 = SUCCESS * -1 = Temporary error * -2 = Permanent error */ int convergence_layer_parse_dataframe(linkaddr_t * source, uint8_t * payload, uint8_t payload_length, uint8_t flags, uint8_t sequence_number, packetbuf_attr_t rssi) { struct mmem * bundlemem = NULL; struct bundle_t * bundle = NULL; struct transmit_ticket_t * ticket = NULL; int n; int length; #if CONVERGENCE_LAYER_SEGMENTATION int ret; #endif /* CONVERGENCE_LAYER_SEGMENTATION */ /* Note down the payload length */ length = payload_length; if( flags != (CONVERGENCE_LAYER_FLAGS_FIRST | CONVERGENCE_LAYER_FLAGS_LAST ) ) { #if CONVERGENCE_LAYER_SEGMENTATION /* We have a multipart bundle here */ if( flags == CONVERGENCE_LAYER_FLAGS_FIRST ) { /* Beginning of a new bundle from a peer, remove old tickets */ for( ticket = list_head(transmission_ticket_list); ticket != NULL; ticket = list_item_next(ticket) ) { if( linkaddr_cmp(&ticket->neighbour, source) && (ticket->flags & CONVERGENCE_LAYER_QUEUE_MULTIPART_RECV) ) { break; } } /* We found a ticket, remove it */ if( ticket != NULL ) { LOG(LOGD_DTN, LOG_CL, LOGL_WRN, "Resynced to peer %u.%u, throwing away old buffer", source->u8[0], source->u8[1]); convergence_layer_free_transmit_ticket(ticket); ticket = NULL; } /* Allocate a new ticket for the incoming bundle */ ticket = convergence_layer_get_transmit_ticket_priority(CONVERGENCE_LAYER_PRIORITY_HIGH); if( ticket == NULL ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Unable to allocate multipart receive ticket"); return -1; } /* Fill the fields of the ticket */ linkaddr_copy(&ticket->neighbour, source); ticket->flags = CONVERGENCE_LAYER_QUEUE_MULTIPART_RECV; ticket->timestamp = clock_time(); ticket->sequence_number = sequence_number; /* Now allocate some memory */ ret = mmem_alloc(&ticket->buffer, length); if( ret < 1 ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Unable to allocate multipart receive buffer of %u bytes", length); convergence_layer_free_transmit_ticket(ticket); ticket = NULL; return -1; } /* Copy the payload into the buffer */ memcpy(MMEM_PTR(&ticket->buffer), payload, length); /* We are waiting for more segments, return now */ return 1; } else { /* Either the middle of the end of a bundle, go look for the ticket */ for( ticket = list_head(transmission_ticket_list); ticket != NULL; ticket = list_item_next(ticket) ) { if( linkaddr_cmp(&ticket->neighbour, source) && (ticket->flags & CONVERGENCE_LAYER_QUEUE_MULTIPART_RECV) ) { break; } } /* Cannot find a ticket, discard segment */ if( ticket == NULL ) { LOG(LOGD_DTN, LOG_CL, LOGL_WRN, "Segment from peer %u.%u does not match any bundles in progress, discarding", source->u8[0], source->u8[1]); return -1; } if( sequence_number != (ticket->sequence_number + 1) % 4 ) { LOG(LOGD_DTN, LOG_CL, LOGL_WRN, "Segment from peer %u.%u is out of sequence. Recv %u, Exp %u", source->u8[0], source->u8[1], sequence_number, (ticket->sequence_number + 1) % 4); return 1; } /* Store the last received and valid sequence number */ ticket->sequence_number = sequence_number; /* Note down the old length to know where to start */ n = ticket->buffer.size; /* Allocate more memory */ ret = mmem_realloc(&ticket->buffer, ticket->buffer.size + length); if( ret < 1 ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Unable to re-allocate multipart receive buffer of %u bytes", ticket->buffer.size + length); convergence_layer_free_transmit_ticket(ticket); return -1; } /* Update timestamp to avoid the ticket from timing out */ ticket->timestamp = clock_time(); /* And append the payload */ memcpy(((uint8_t *) MMEM_PTR(&ticket->buffer)) + n, payload, length); } if( flags & CONVERGENCE_LAYER_FLAGS_LAST ) { /* We have the last segment, change pointer so that the rest of the function works as planned */ payload = (uint8_t *) MMEM_PTR(&ticket->buffer); length = ticket->buffer.size; LOG(LOGD_DTN, LOG_CL, LOGL_DBG, "%u byte multipart bundle received from %u.%u, parsing", length, source->u8[0], source->u8[1]); } else { /* We are waiting for more segments, return now */ return 1; } #else /* CONVERGENCE_LAYER_SEGMENTATION */ /* We will never be able to parse that bundle, signal a permanent error */ return -2; #endif /* CONVERGENCE_LAYER_SEGMENTATION */ } /* Allocate memory, parse the bundle and set reference counter to 1 */ bundlemem = bundle_recover_bundle(payload, length); /* We do not need the ticket anymore if there was one, deallocate it */ if( ticket != NULL ) { convergence_layer_free_transmit_ticket(ticket); ticket = NULL; } if( !bundlemem ) { LOG(LOGD_DTN, LOG_CL, LOGL_WRN, "Error recovering bundle"); /* Possibly not enough memory -> temporary error */ return -1; } bundle = (struct bundle_t *) MMEM_PTR(bundlemem); if( !bundle ) { LOG(LOGD_DTN, LOG_CL, LOGL_WRN, "Invalid bundle pointer"); bundle_decrement(bundlemem); /* Possibly not enough memory -> temporary error */ return -1; } /* Check for bundle expiration */ if( bundle_ageing_is_expired(bundlemem) ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Bundle received from %u.%u with SeqNo %u is expired", source->u8[0], source->u8[1], sequence_number); bundle_decrement(bundlemem); /* Send permanent rejection */ return -2; } /* Mark the bundle as "internal" */ bundle->source_process = &agent_process; LOG(LOGD_DTN, LOG_CL, LOGL_DBG, "Bundle from ipn:%lu.%lu (to ipn:%lu.%lu) received from %u.%u with SeqNo %u", bundle->src_node, bundle->src_srv, bundle->dst_node, bundle->dst_srv, source->u8[0], source->u8[1], sequence_number); /* Store the node from which we received the bundle */ linkaddr_copy(&bundle->msrc, source); /* Store the RSSI for this packet */ bundle->rssi = rssi; /* Hand over the bundle to dispatching */ n = dispatching_dispatch_bundle(bundlemem); bundlemem = NULL; if( n ) { /* Dispatching was successfull! */ return 1; } /* Temporary error */ return -1; }
int convergence_layer_send_bundle(struct transmit_ticket_t * ticket) { struct bundle_t *bundle = NULL; uint16_t length = 0; uint8_t * buffer = NULL; uint8_t buffer_length = 0; #if CONVERGENCE_LAYER_SEGMENTATION int ret; int segments; #endif /* CONVERGENCE_LAYER_SEGMENTATION */ LOG(LOGD_DTN, LOG_CL, LOGL_DBG, "Sending bundle %lu to %u.%u with ticket %p", ticket->bundle_number, ticket->neighbour.u8[0], ticket->neighbour.u8[1], ticket); if( !(ticket->flags & CONVERGENCE_LAYER_QUEUE_MULTIPART) ) { /* Read the bundle from storage, if it is not in memory */ if( ticket->bundle == NULL ) { ticket->bundle = BUNDLE_STORAGE.read_bundle(ticket->bundle_number); if( ticket->bundle == NULL ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Unable to read bundle %lu", ticket->bundle_number); /* FIXME: Notify somebody */ return -1; } } /* Get our bundle struct and check the pointer */ bundle = (struct bundle_t *) MMEM_PTR(ticket->bundle); if( bundle == NULL ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Invalid bundle pointer for bundle %lu", ticket->bundle_number); bundle_decrement(ticket->bundle); ticket->bundle = NULL; return -1; } /* Check if bundle has expired */ if( bundle_ageing_is_expired(ticket->bundle) ) { LOG(LOGD_DTN, LOG_CL, LOGL_INF, "Bundle %lu has expired, not sending it", ticket->bundle_number); /* Bundle is expired */ bundle_decrement(ticket->bundle); /* Tell storage to delete - it will take care of the rest */ BUNDLE_STORAGE.del_bundle(ticket->bundle_number, REASON_LIFETIME_EXPIRED); return -1; } } /* Get the outgoing network buffer */ buffer = dtn_network_get_buffer(); if( buffer == NULL ) { bundle_decrement(ticket->bundle); ticket->bundle = NULL; return -1; } /* Get the buffer length */ buffer_length = dtn_network_get_buffer_length(); #if CONVERGENCE_LAYER_SEGMENTATION /* We have to use a heuristic to estimate if the bundle will be a multipart bundle */ if( ticket->bundle->size > CONVERGENCE_LAYER_MAX_LENGTH && !(ticket->flags & CONVERGENCE_LAYER_QUEUE_MULTIPART) ) { /* This is a bundle for multiple segments and we have our first look at it */ ticket->flags |= CONVERGENCE_LAYER_QUEUE_MULTIPART; LOG(LOGD_DTN, LOG_CL, LOGL_DBG, "Encoding multipart bundle %lu", ticket->bundle_number); /* Now allocate a buffer to serialize the bundle * The size is a rough estimation here and will be reallocated later on */ ret = mmem_alloc(&ticket->buffer, ticket->bundle->size); if( ret < 1 ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Multipart bundle %lu could not be encoded, not enough memory for %u bytes", ticket->bundle_number, ticket->bundle->size); ticket->flags &= ~CONVERGENCE_LAYER_QUEUE_MULTIPART; return -1; } /* Encode the bundle into our temporary buffer */ length = bundle_encode_bundle(ticket->bundle, (uint8_t *) MMEM_PTR(&ticket->buffer), ticket->buffer.size); if( length < 0 ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Multipart bundle %lu could not be encoded, error occured", ticket->bundle_number); mmem_free(&ticket->buffer); ticket->buffer.ptr = NULL; ticket->flags &= ~CONVERGENCE_LAYER_QUEUE_MULTIPART; return -1; } /* Decrease memory size to what is actually needed */ ret = mmem_realloc(&ticket->buffer, length); if( ret < 1 ) { LOG(LOGD_DTN, LOG_CL, LOGL_ERR, "Multipart bundle %lu could not be encoded, realloc failed", ticket->bundle_number); mmem_free(&ticket->buffer); ticket->buffer.ptr = NULL; ticket->flags &= ~CONVERGENCE_LAYER_QUEUE_MULTIPART; return -1; } /* We do not need the original bundle anymore */ bundle_decrement(ticket->bundle); ticket->bundle = NULL; /* Initialize the state for this bundle */ ticket->offset_sent = 0; ticket->offset_acked = 0; ticket->sequence_number = outgoing_sequence_number; /* Calculate the number of segments we will need */ segments = (length + 0.5 * CONVERGENCE_LAYER_MAX_LENGTH) / CONVERGENCE_LAYER_MAX_LENGTH; /* And reserve the sequence number space for this bundle to allow for consequtive numbers */ outgoing_sequence_number = (outgoing_sequence_number + segments) % 4; } /* Initialize the header field */ buffer[0] = CONVERGENCE_LAYER_TYPE_DATA & CONVERGENCE_LAYER_MASK_TYPE; /* Check if this is a multipart bundle */ if( ticket->flags & CONVERGENCE_LAYER_QUEUE_MULTIPART ) { /* Calculate the remaining length */ length = ticket->buffer.size - ticket->offset_acked; /* Is it possible, that we send a single-part bundle here because the heuristic * from above failed. So be it. */ if( length <= CONVERGENCE_LAYER_MAX_LENGTH && ticket->offset_acked == 0 ) { /* One bundle per segment, standard flags */ buffer[0] |= (CONVERGENCE_LAYER_FLAGS_FIRST | CONVERGENCE_LAYER_FLAGS_LAST) & CONVERGENCE_LAYER_MASK_FLAGS; } else if( ticket->offset_acked == 0 ) { /* First segment of a bundle */ buffer[0] |= CONVERGENCE_LAYER_FLAGS_FIRST & CONVERGENCE_LAYER_MASK_FLAGS; } else if( length <= CONVERGENCE_LAYER_MAX_LENGTH ) { /* Last segment of a bundle */ buffer[0] |= CONVERGENCE_LAYER_FLAGS_LAST & CONVERGENCE_LAYER_MASK_FLAGS; } else if( length > CONVERGENCE_LAYER_MAX_LENGTH) { /* A segment in the middle of a bundle */ buffer[0] &= ~CONVERGENCE_LAYER_MASK_FLAGS; } /* one byte for the CL header */ length += 1; if( length > CONVERGENCE_LAYER_MAX_LENGTH ) { length = CONVERGENCE_LAYER_MAX_LENGTH; } if( length > buffer_length ) { length = buffer_length; } /* Copy the subset of the bundle into the buffer */ memcpy(buffer + 1, ((uint8_t *) MMEM_PTR(&ticket->buffer)) + ticket->offset_acked, length - 1); /* Every segment so far has been acked */ if( ticket->offset_sent == ticket->offset_acked ) { /* It is the first time that we are sending this segment */ ticket->offset_sent += length - 1; /* Increment the sequence number for the new segment, except for the first segment */ if( ticket->offset_sent != 0 ) { ticket->sequence_number = (ticket->sequence_number + 1) % 4; } } } else { #endif /* CONVERGENCE_LAYER_SEGMENTATION */ /* one byte for the CL header */ length = 1; /* Initialize the header field */ buffer[0] = CONVERGENCE_LAYER_TYPE_DATA & CONVERGENCE_LAYER_MASK_TYPE; /* One bundle per segment, standard flags */ buffer[0] |= (CONVERGENCE_LAYER_FLAGS_FIRST | CONVERGENCE_LAYER_FLAGS_LAST) & CONVERGENCE_LAYER_MASK_FLAGS; /* Encode the bundle into the buffer */ length += bundle_encode_bundle(ticket->bundle, buffer + 1, buffer_length - 1); /* Initialize the sequence number */ ticket->sequence_number = outgoing_sequence_number; outgoing_sequence_number = (outgoing_sequence_number + 1) % 4; #if CONVERGENCE_LAYER_SEGMENTATION } #endif /* CONVERGENCE_LAYER_SEGMENTATION */ /* Put the sequence number for this bundle into the outgoing header */ buffer[0] |= (ticket->sequence_number << 2) & CONVERGENCE_LAYER_MASK_SEQNO; /* Flag the bundle as being in transit now */ ticket->flags |= CONVERGENCE_LAYER_QUEUE_IN_TRANSIT; /* Now we are transmitting */ convergence_layer_transmitting = 1; /* This neighbour is blocked, until we have received the App Layer ACK or NACK */ convergence_layer_set_blocked(&ticket->neighbour); /* And send it out */ dtn_network_send(&ticket->neighbour, length, (void *) ticket); return 1; }