/* * Copyright (c) 2011 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup tcp * @{ */ /** * @file Segment processing */ #include #include #include #include "segment.h" #include "seq_no.h" #include "tcp_type.h" /** Alocate new segment structure. */ static tcp_segment_t *tcp_segment_new(void) { return calloc(1, sizeof(tcp_segment_t)); } /** Delete segment. */ void tcp_segment_delete(tcp_segment_t *seg) { free(seg->dfptr); free(seg); } /** Create duplicate of segment. * * @param seg Segment * @return Duplicate segment */ tcp_segment_t *tcp_segment_dup(tcp_segment_t *seg) { tcp_segment_t *scopy; size_t tsize; scopy = tcp_segment_new(); if (scopy == NULL) return NULL; scopy->ctrl = seg->ctrl; scopy->seq = seg->seq; scopy->ack = seg->ack; scopy->len = seg->len; scopy->wnd = seg->wnd; scopy->up = seg->up; tsize = tcp_segment_text_size(seg); scopy->data = calloc(tsize, 1); if (scopy->data == NULL) { free(scopy); return NULL; } memcpy(scopy->data, seg->data, tsize); scopy->dfptr = scopy->data; return scopy; } /** Create a control-only segment. * * @return Segment */ tcp_segment_t *tcp_segment_make_ctrl(tcp_control_t ctrl) { tcp_segment_t *seg; seg = tcp_segment_new(); if (seg == NULL) return NULL; seg->ctrl = ctrl; seg->len = seq_no_control_len(ctrl); return seg; } /** Create an RST segment. * * @param seg Segment we are replying to * @return RST segment */ tcp_segment_t *tcp_segment_make_rst(tcp_segment_t *seg) { tcp_segment_t *rseg; rseg = tcp_segment_new(); if (rseg == NULL) return NULL; if ((seg->ctrl & CTL_ACK) == 0) { rseg->ctrl = CTL_RST | CTL_ACK; rseg->seq = 0; rseg->ack = seg->seq + seg->len; } else { rseg->ctrl = CTL_RST; rseg->seq = seg->ack; } return rseg; } /** Create a control segment. * * @return Segment */ tcp_segment_t *tcp_segment_make_data(tcp_control_t ctrl, void *data, size_t size) { tcp_segment_t *seg; seg = tcp_segment_new(); if (seg == NULL) return NULL; seg->ctrl = ctrl; seg->len = seq_no_control_len(ctrl) + size; seg->dfptr = seg->data = malloc(size); if (seg->dfptr == NULL) { free(seg); return NULL; } memcpy(seg->data, data, size); return seg; } /** Trim segment from left and right by the specified amount. * * Trim any text or control to remove the specified amount of sequence * numbers from the left (lower sequence numbers) and right side * (higher sequence numbers) of the segment. * * @param seg Segment, will be modified in place * @param left Amount of sequence numbers to trim from the left * @param right Amount of sequence numbers to trim from the right */ void tcp_segment_trim(tcp_segment_t *seg, uint32_t left, uint32_t right) { uint32_t t_size; assert(left + right <= seg->len); /* Special case, entire segment trimmed from left */ if (left == seg->len) { seg->seq = seg->seq + seg->len; seg->len = 0; return; } /* Special case, entire segment trimmed from right */ if (right == seg->len) { seg->len = 0; return; } /* General case */ t_size = tcp_segment_text_size(seg); if (left > 0 && (seg->ctrl & CTL_SYN) != 0) { /* Trim SYN */ seg->ctrl &= ~CTL_SYN; seg->seq++; seg->len--; left--; } if (right > 0 && (seg->ctrl & CTL_FIN) != 0) { /* Trim FIN */ seg->ctrl &= ~CTL_FIN; seg->len--; right--; } if (left > 0 || right > 0) { /* Trim segment text */ assert(left + right <= t_size); seg->data += left; seg->len -= left + right; } } /** Copy out text data from segment. * * Data is copied from the beginning of the segment text up to @a size bytes. * @a size must not be greater than the size of the segment text, but * it can be less. * * @param seg Segment * @param buf Destination buffer * @param size Size of destination buffer */ void tcp_segment_text_copy(tcp_segment_t *seg, void *buf, size_t size) { assert(size <= tcp_segment_text_size(seg)); memcpy(buf, seg->data, size); } /** Return number of bytes in segment text. * * @param seg Segment * @return Number of bytes in segment text */ size_t tcp_segment_text_size(tcp_segment_t *seg) { return seg->len - seq_no_control_len(seg->ctrl); } /** Dump segment contents to log. * * @param seg Segment */ void tcp_segment_dump(tcp_segment_t *seg) { log_msg(LOG_DEFAULT, LVL_DEBUG2, "Segment dump:"); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - ctrl = %u", (unsigned)seg->ctrl); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - seq = %" PRIu32, seg->seq); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - ack = %" PRIu32, seg->ack); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - len = %" PRIu32, seg->len); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - wnd = %" PRIu32, seg->wnd); log_msg(LOG_DEFAULT, LVL_DEBUG2, " - up = %" PRIu32, seg->up); } /** * @} */