Home | History | Annotate | Download | only in tcp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 1997-2001, 2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/types.h>
     30 #include <sys/kmem.h>
     31 #include <sys/debug.h>
     32 #include <netinet/tcp.h>
     33 #include <sys/systm.h>
     34 #include <sys/stropts.h>
     35 #include <netinet/in.h>
     36 #include <netinet/ip6.h>
     37 #include <inet/common.h>
     38 #include <inet/ip.h>
     39 #include <inet/tcp.h>
     40 
     41 /*
     42  * To insert a new blk to the array of SACK blk in receiver.
     43  *
     44  * Parameters:
     45  *	sack_blk_t *head: pointer to the array of SACK blks.
     46  *	tcp_seq begin: starting seq num of the new blk.
     47  *	tcp_seq end: ending seq num of the new blk.
     48  *	int32_t *num: (referenced) total num of SACK blks on the list.
     49  */
     50 void
     51 tcp_sack_insert(sack_blk_t *head, tcp_seq begin, tcp_seq end, int32_t *num)
     52 {
     53 	int32_t	i, j, old_num, new_num;
     54 	sack_blk_t tmp[MAX_SACK_BLK - 1];
     55 
     56 	/* The array is empty, just add the new one. */
     57 	if (*num == 0) {
     58 		head[0].begin = begin;
     59 		head[0].end = end;
     60 		*num = 1;
     61 		return;
     62 	}
     63 
     64 	/*
     65 	 * Check for overlap.  There are five cases.
     66 	 *
     67 	 * 1. there is no overlap with any other SACK blks.
     68 	 * 2. new SACK blk is completely contained in another blk.
     69 	 * 3. tail part of new SACK blk overlaps with another blk.
     70 	 * 4. head part of new SACK blk overlaps with another blk.
     71 	 * 5. new SACK blk completely contains another blk.
     72 	 *
     73 	 * Use tmp to hold old SACK blks.  After the loop, copy them back
     74 	 * to head.
     75 	 */
     76 	old_num = *num;
     77 	if (old_num > MAX_SACK_BLK - 1) {
     78 		old_num = MAX_SACK_BLK - 1;
     79 	}
     80 	new_num = old_num;
     81 	j = 0;
     82 	for (i = 0; i < old_num; i++) {
     83 		if (SEQ_LT(end, head[i].begin) || SEQ_GT(begin, head[i].end)) {
     84 			/* Case 1: continue to check. */
     85 			tmp[j].begin = head[i].begin;
     86 			tmp[j].end = head[i].end;
     87 			j++;
     88 			continue;
     89 		} else if (SEQ_GEQ(begin, head[i].begin) &&
     90 		    SEQ_LEQ(end, head[i].end)) {
     91 			/* Case 2: re-insert the old blk to the head. */
     92 			begin = head[i].begin;
     93 			end = head[i].end;
     94 		} else if (SEQ_LEQ(end, head[i].end) &&
     95 		    SEQ_GEQ(end, head[i].begin)) {
     96 			/*
     97 			 * Case 3: Extend the new blk, remove the old one
     98 			 * and continue to check.
     99 			 */
    100 			end = head[i].end;
    101 		} else if (SEQ_GEQ(begin, head[i].begin) &&
    102 		    SEQ_LEQ(begin, head[i].end)) {
    103 			/* Case 4 */
    104 			begin = head[i].begin;
    105 		}
    106 		/*
    107 		 * Common code for all cases except the first one, which
    108 		 * copies the original SACK blk into the tmp storage.  Other
    109 		 * cases remove the original SACK blk by not copying into
    110 		 * tmp storage.
    111 		 */
    112 		new_num--;
    113 	}
    114 
    115 	head[0].begin = begin;
    116 	head[0].end = end;
    117 	for (i = 0; i < new_num; i++) {
    118 		head[i+1].begin = tmp[i].begin;
    119 		head[i+1].end = tmp[i].end;
    120 	}
    121 	*num = new_num + 1;
    122 }
    123 
    124 
    125 /*
    126  * To remove a SACK block.
    127  *
    128  * Parameters:
    129  *	sack_blk_t *head: pointer to the array of SACK blks.
    130  *	tcp_seq end: to remove all sack blk with seq num less than end.
    131  *	int32_t *num: (referenced) total num of SACK blks in the array.
    132  */
    133 void
    134 tcp_sack_remove(sack_blk_t *head, tcp_seq end, int32_t *num)
    135 {
    136 	sack_blk_t tmp[MAX_SACK_BLK];
    137 	int32_t i, j, old_num, new_num;
    138 
    139 	if (*num == 0)
    140 		return;
    141 
    142 	old_num = *num;
    143 	new_num = old_num;
    144 	j = 0;
    145 	/* Walk thru the whole list and copy the new list to tmp[]. */
    146 	for (i = 0; i < old_num; i++) {
    147 		if (SEQ_GT(end, head[i].begin)) {
    148 			/*
    149 			 * Check to see if the old SACK blk needs to be
    150 			 * removed or updated.  If the old blk is just
    151 			 * partially covered, update begin and continue.
    152 			 * If the old blk is completely covered, remove it
    153 			 * and continue to check.
    154 			 */
    155 			if (SEQ_GEQ(end, head[i].end)) {
    156 				new_num--;
    157 				continue;
    158 			} else {
    159 				tmp[j].begin = end;
    160 				tmp[j].end = head[i].end;
    161 			}
    162 		} else {
    163 			tmp[j].begin = head[i].begin;
    164 			tmp[j].end = head[i].end;
    165 		}
    166 		j++;
    167 	}
    168 	/* Copy tmp[] back to the original list. */
    169 	for (i = 0; i < new_num; i++) {
    170 		head[i].begin = tmp[i].begin;
    171 		head[i].end = tmp[i].end;
    172 	}
    173 	*num = new_num;
    174 }
    175 
    176 
    177 /*
    178  * Use the SACK info to insert a "notsack'ed" blk.  The notsack'ed blk list
    179  * contains the list of blks which have not been selectively acknowledged
    180  * by the receiver.  The SACK info is a blk which is being selectively
    181  * acknowledged by the receiver.
    182  *
    183  * Parameters:
    184  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
    185  *		blks.
    186  *	tcp_seq begin: starting seq num of the SACK info.
    187  *	tcp_seq end: ending seq num of the SACK info.
    188  *	int32_t *num: (referenced) total num of notsack'ed blk on the list.
    189  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
    190  *		blks.
    191  */
    192 void
    193 tcp_notsack_insert(notsack_blk_t **head, tcp_seq begin, tcp_seq end,
    194     int32_t *num, uint32_t *sum)
    195 {
    196 	notsack_blk_t *prev, *tmp, *new;
    197 	uint32_t tmp_sum, tmp_num;
    198 
    199 	if (*head == NULL) {
    200 		return;
    201 	}
    202 
    203 	tmp = *head;
    204 	prev = NULL;
    205 	/* Find the right place of updating the list. */
    206 	while ((tmp != NULL) && SEQ_LEQ(tmp->end, begin)) {
    207 		prev = tmp;
    208 		(tmp->sack_cnt)++;
    209 		tmp = tmp->next;
    210 	}
    211 
    212 	/*
    213 	 * This can happen only when TCP sends new data but the notsack list
    214 	 * is not updated.
    215 	 */
    216 	if (tmp == NULL) {
    217 		return;
    218 	}
    219 
    220 	/*
    221 	 * This means the new SACK info covers something that is not on
    222 	 * the list anymore.
    223 	 */
    224 	if (SEQ_LEQ(end, tmp->begin)) {
    225 		return;
    226 	}
    227 
    228 	/* The SACK info covers up to this blk.  So just check for this blk. */
    229 	if (SEQ_LEQ(end, tmp->end)) {
    230 		/*
    231 		 * Only this notsack'ed blk is completely covered.  Delete
    232 		 * it and return.
    233 		 */
    234 		if (end == tmp->end && SEQ_LEQ(begin, tmp->begin)) {
    235 			if (prev != NULL) {
    236 				prev->next = tmp->next;
    237 			} else {
    238 				*head = tmp->next;
    239 			}
    240 			(*num)--;
    241 			*sum -= tmp->end - tmp->begin;
    242 			kmem_free(tmp, sizeof (notsack_blk_t));
    243 			return;
    244 		}
    245 		/* This blk is partially covered. */
    246 		if (SEQ_GEQ(begin, tmp->begin)) {
    247 			/* Check what needs to be updated. */
    248 			if (begin == tmp->begin) {
    249 				*sum -= end - tmp->begin;
    250 				tmp->begin = end;
    251 			} else if (end == tmp->end) {
    252 				*sum -= tmp->end - begin;
    253 				tmp->end = begin;
    254 				(tmp->sack_cnt)++;
    255 			} else {
    256 				/* Split the notsack blk. */
    257 				if ((new = kmem_alloc(sizeof (notsack_blk_t),
    258 				    KM_NOSLEEP)) == NULL) {
    259 					return;
    260 				}
    261 				new->end = tmp->end;
    262 				new->begin = end;
    263 				new->next = tmp->next;
    264 				new->sack_cnt = 0;
    265 				tmp->end = begin;
    266 				tmp->next = new;
    267 				(tmp->sack_cnt)++;
    268 				(*num)++;
    269 				*sum -= end - begin;
    270 			}
    271 		} else {
    272 			*sum -= end - tmp->begin;
    273 			tmp->begin = end;
    274 		}
    275 		return;
    276 	}
    277 
    278 	/* Need to check for coverage of this blk and later blks. */
    279 	tmp_sum = *sum;
    280 	tmp_num = *num;
    281 	if (SEQ_LT(tmp->begin, begin)) {
    282 		tmp_sum -= tmp->end - begin;
    283 		tmp->end = begin;
    284 		(tmp->sack_cnt)++;
    285 		prev = tmp;
    286 		tmp = tmp->next;
    287 	}
    288 
    289 	while (tmp != NULL) {
    290 		/* The coverage stops here. */
    291 		if (SEQ_GT(tmp->begin, end)) {
    292 			break;
    293 		} else {
    294 			/* Is the blk completely or partially covered? */
    295 			if (SEQ_LEQ(tmp->end, end)) {
    296 				tmp_num--;
    297 				tmp_sum -= tmp->end - tmp->begin;
    298 				if (prev != NULL) {
    299 					prev->next = tmp->next;
    300 					kmem_free(tmp, sizeof (notsack_blk_t));
    301 					tmp = prev->next;
    302 				} else {
    303 					*head = tmp->next;
    304 					kmem_free(tmp, sizeof (notsack_blk_t));
    305 					tmp = *head;
    306 				}
    307 			} else {
    308 				/*
    309 				 * This blk is partially covered.  It also
    310 				 * means it should be the end of coverage.
    311 				 */
    312 				tmp_sum -= end - tmp->begin;
    313 				tmp->begin = end;
    314 				break;
    315 			}
    316 		}
    317 	}
    318 	*num = tmp_num;
    319 	*sum = tmp_sum;
    320 }
    321 
    322 
    323 /*
    324  * To remove notsack'ed blks.
    325  *
    326  * Parameters:
    327  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
    328  *		blks.
    329  *	tcp_seq end: to remove all notsack'ed blk with seq num less than end.
    330  *	int32_t *num: (referenced) total num of notsack'ed blks.
    331  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
    332  *		blks.
    333  */
    334 void
    335 tcp_notsack_remove(notsack_blk_t **head, tcp_seq end, int32_t *num,
    336     uint32_t *sum)
    337 {
    338 	notsack_blk_t *prev, *tmp;
    339 	uint32_t tmp_sum = *sum;
    340 
    341 	if (*head == NULL)
    342 		return;
    343 
    344 	prev = NULL;
    345 	tmp = *head;
    346 	while (tmp != NULL) {
    347 		/* There is nothing to discard. */
    348 		if (SEQ_GT(tmp->begin, end)) {
    349 			break;
    350 		}
    351 
    352 		/* Is the blk completely or partially covered? */
    353 		if (SEQ_GEQ(end, tmp->end)) {
    354 			(*num)--;
    355 			tmp_sum -= tmp->end - tmp->begin;
    356 			if (prev == NULL) {
    357 				*head = tmp->next;
    358 				kmem_free(tmp, sizeof (notsack_blk_t));
    359 				tmp = *head;
    360 			} else {
    361 				prev->next = tmp->next;
    362 				kmem_free(tmp, sizeof (notsack_blk_t));
    363 				tmp = prev->next;
    364 			}
    365 		} else {
    366 			tmp_sum -= end - tmp->begin;
    367 			tmp->begin = end;
    368 			break;
    369 		}
    370 	}
    371 	*sum = tmp_sum;
    372 }
    373 
    374 
    375 /*
    376  * To update the notsack'ed list when new data is sent.
    377  *
    378  * Assumption: this should only be called when new notsack blk is to be added.
    379  *
    380  * Parameters:
    381  *	notsack_blk_t **head: address of the pointer to the list of notsack'ed
    382  *		blks.
    383  *	tcp_seq begin: beginning seq num of new data.
    384  *	tcp_seq end: ending seq num of new data.
    385  *	int32_t *num: (referenced) total num of notsack'ed blks.
    386  *	uint32_t *sum: (referenced) total num of bytes of all the notsack'ed
    387  *		blks.
    388  */
    389 void tcp_notsack_update(notsack_blk_t **head, tcp_seq begin, tcp_seq end,
    390     int32_t *num, uint32_t *sum)
    391 {
    392 	notsack_blk_t *tmp;
    393 
    394 	tmp = *head;
    395 	/* If the list is empty, create a new one. */
    396 	if (tmp == NULL) {
    397 		if ((tmp = kmem_alloc(sizeof (notsack_blk_t), KM_NOSLEEP)) ==
    398 		    NULL) {
    399 			return;
    400 		}
    401 		tmp->begin = begin;
    402 		tmp->end = end;
    403 		tmp->next = NULL;
    404 		tmp->sack_cnt = 0;
    405 		*head = tmp;
    406 		*num = 1;
    407 		*sum = end - begin;
    408 		return;
    409 	}
    410 
    411 	/*
    412 	 * Find the place to add the new blk.  This assumes that new data
    413 	 * is being sent, so the place to insert the new notsack blk is at
    414 	 * the end of the list.
    415 	 */
    416 	while (tmp->next != NULL) {
    417 		tmp = tmp->next;
    418 	}
    419 
    420 	/* Does the new blk overlap with old one? */
    421 	if (SEQ_GEQ(tmp->end, begin)) {
    422 		*sum += end - tmp->end;
    423 		tmp->end = end;
    424 	} else {
    425 		/* No.  Need to create a new notsack blk. */
    426 		tmp->next = kmem_alloc(sizeof (notsack_blk_t), KM_NOSLEEP);
    427 		if (tmp->next != NULL) {
    428 			tmp = tmp->next;
    429 			tmp->begin = begin;
    430 			tmp->end = end;
    431 			tmp->next = NULL;
    432 			tmp->sack_cnt = 0;
    433 			(*num)++;
    434 			*sum += end - begin;
    435 		}
    436 	}
    437 }
    438