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