The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/tcp_stacks/sack_filter.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2017-9 Netflix, Inc.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  *
   25  */
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 #ifndef _KERNEL
   29 #define _WANT_TCPCB 1
   30 #endif
   31 #include <sys/types.h>
   32 #include <sys/queue.h>
   33 #include <sys/socket.h>
   34 #ifdef _KERNEL
   35 #include <sys/mbuf.h>
   36 #include <sys/sockopt.h>
   37 #endif
   38 #include <netinet/in.h>
   39 #include <netinet/in_pcb.h>
   40 #include <netinet/tcp.h>
   41 #include <netinet/tcp_var.h>
   42 #include <netinet/tcp_seq.h>
   43 #ifndef _KERNEL
   44 #include <stdio.h>
   45 #include <unistd.h>
   46 #include <string.h>
   47 #include <strings.h>
   48 #include <stdlib.h>
   49 #include <limits.h>
   50 #include <getopt.h>
   51 #endif
   52 #include "sack_filter.h"
   53 
   54 /*
   55  * Sack filter is used to filter out sacks
   56  * that have already been processed. The idea
   57  * is pretty simple really, consider two sacks
   58  *
   59  * SACK 1
   60  *   cum-ack A
   61  *     sack B - C
   62  * SACK 2
   63  *   cum-ack A
   64  *     sack D - E
   65  *     sack B - C
   66  *
   67  * The previous sack information (B-C) is repeated
   68  * in SACK 2. If the receiver gets SACK 1 and then
   69  * SACK 2 then any work associated with B-C as already
   70  * been completed. This only effects where we may have
   71  * (as in bbr or rack) cases where we walk a linked list.
   72  *
   73  * Now the utility trys to keep everything in a single
   74  * cache line. This means that its not perfect and
   75  * it could be that so big of sack's come that a
   76  * "remembered" processed sack falls off the list and
   77  * so gets re-processed. Thats ok, it just means we
   78  * did some extra work. We could of course take more
   79  * cache line hits by expanding the size of this
   80  * structure, but then that would cost more.
   81  */
   82 
   83 #ifndef _KERNEL
   84 int detailed_dump = 0;
   85 uint64_t cnt_skipped_oldsack = 0;
   86 uint64_t cnt_used_oldsack = 0;
   87 int highest_used=0;
   88 int over_written=0;
   89 int empty_avail=0;
   90 int no_collapse = 0;
   91 FILE *out = NULL;
   92 FILE *in = NULL;
   93 #endif
   94 
   95 #define sack_blk_used(sf, i) ((1 << i) & sf->sf_bits)
   96 #define sack_blk_set(sf, i) ((1 << i) | sf->sf_bits)
   97 #define sack_blk_clr(sf, i) (~(1 << i) & sf->sf_bits)
   98 
   99 #ifndef _KERNEL
  100 static
  101 #endif
  102 void
  103 sack_filter_clear(struct sack_filter *sf, tcp_seq seq)
  104 {
  105         sf->sf_ack = seq;
  106         sf->sf_bits = 0;
  107         sf->sf_cur = 0;
  108         sf->sf_used = 0;
  109 }
  110 /*
  111  * Given a previous sack filter block, filter out
  112  * any entries where the cum-ack moves over them
  113  * fully or partially.
  114  */
  115 static void
  116 sack_filter_prune(struct sack_filter *sf, tcp_seq th_ack)
  117 {
  118         int32_t i;
  119         /* start with the oldest */
  120         for (i = 0; i < SACK_FILTER_BLOCKS; i++) {
  121                 if (sack_blk_used(sf, i)) {
  122                         if (SEQ_GT(th_ack, sf->sf_blks[i].end)) {
  123                                 /* This block is consumed */
  124                                 sf->sf_bits = sack_blk_clr(sf, i);
  125                                 sf->sf_used--;
  126                         } else if (SEQ_GT(th_ack, sf->sf_blks[i].start)) {
  127                                 /* Some of it is acked */
  128                                 sf->sf_blks[i].start = th_ack;
  129                                 /* We could in theory break here, but
  130                                  * there are some broken implementations
  131                                  * that send multiple blocks. We want
  132                                  * to catch them all with similar seq's.
  133                                  */
  134                         }
  135                 }
  136         }
  137         sf->sf_ack = th_ack;
  138 }
  139 
  140 /*
  141  * Return true if you find that
  142  * the sackblock b is on the score
  143  * board. Update it along the way
  144  * if part of it is on the board.
  145  */
  146 static int32_t
  147 is_sack_on_board(struct sack_filter *sf, struct sackblk *b)
  148 {
  149         int32_t i, cnt;
  150 
  151         for (i = sf->sf_cur, cnt=0; cnt < SACK_FILTER_BLOCKS; cnt++) {
  152                 if (sack_blk_used(sf, i)) {
  153                         if (SEQ_LT(b->start, sf->sf_ack)) {
  154                                 /* Behind cum-ack update */
  155                                 b->start = sf->sf_ack;
  156                         }
  157                         if (SEQ_LT(b->end, sf->sf_ack)) {
  158                                 /* End back behind too */
  159                                 b->end = sf->sf_ack;
  160                         }
  161                         if (b->start == b->end) {
  162                                 return(1);
  163                         }
  164                         /* Jonathans Rule 1 */
  165                         if (SEQ_LEQ(sf->sf_blks[i].start, b->start) &&
  166                             SEQ_GEQ(sf->sf_blks[i].end, b->end)) {
  167                                 /**
  168                                  * Our board has this entirely in
  169                                  * whole or in part:
  170                                  *
  171                                  * board  |-------------|
  172                                  * sack   |-------------|
  173                                  * <or>
  174                                  * board  |-------------|
  175                                  * sack       |----|
  176                                  *
  177                                  */
  178                                 return(1);
  179                         }
  180                         /* Jonathans Rule 2 */
  181                         if(SEQ_LT(sf->sf_blks[i].end, b->start)) {
  182                                 /**
  183                                  * Not near each other:
  184                                  *
  185                                  * board   |---|
  186                                  * sack           |---|
  187                                  */
  188                                 goto nxt_blk;
  189                         }
  190                         /* Jonathans Rule 3 */
  191                         if (SEQ_GT(sf->sf_blks[i].start, b->end)) {
  192                                 /**
  193                                  * Not near each other:
  194                                  *
  195                                  * board         |---|
  196                                  * sack  |---|
  197                                  */
  198                                 goto nxt_blk;
  199                         }
  200                         if (SEQ_LEQ(sf->sf_blks[i].start, b->start)) {
  201                                 /**
  202                                  * The board block partial meets:
  203                                  *
  204                                  *  board   |--------|
  205                                  *  sack        |----------|
  206                                  *    <or>
  207                                  *  board   |--------|
  208                                  *  sack    |--------------|
  209                                  *
  210                                  * up with this one (we have part of it).
  211                                  * 1) Update the board block to the new end
  212                                  *      and
  213                                  * 2) Update the start of this block to my end.
  214                                  */
  215                                 b->start = sf->sf_blks[i].end;
  216                                 sf->sf_blks[i].end = b->end;
  217                                 goto nxt_blk;
  218                         }
  219                         if (SEQ_GEQ(sf->sf_blks[i].end, b->end)) {
  220                                 /**
  221                                  * The board block partial meets:
  222                                  *
  223                                  *  board       |--------|
  224                                  *  sack  |----------|
  225                                  *     <or>
  226                                  *  board       |----|
  227                                  *  sack  |----------|
  228                                  * 1) Update the board block to the new start
  229                                  *      and
  230                                  * 2) Update the start of this block to my end.
  231                                  */
  232                                 b->end = sf->sf_blks[i].start;
  233                                 sf->sf_blks[i].start = b->start;
  234                                 goto nxt_blk;
  235                         }
  236                 }
  237         nxt_blk:
  238                 i++;
  239                 i %= SACK_FILTER_BLOCKS;
  240         }
  241         /* Did we totally consume it in pieces? */
  242         if (b->start != b->end)
  243                 return(0);
  244         else
  245                 return(1);
  246 }
  247 
  248 static int32_t
  249 sack_filter_old(struct sack_filter *sf, struct sackblk *in, int  numblks)
  250 {
  251         int32_t num, i;
  252         struct sackblk blkboard[TCP_MAX_SACK];
  253         /*
  254          * An old sack has arrived. It may contain data
  255          * we do not have. We might not have it since
  256          * we could have had a lost ack <or> we might have the
  257          * entire thing on our current board. We want to prune
  258          * off anything we have. With this function though we
  259          * won't add to the board.
  260          */
  261         for( i = 0, num = 0; i<numblks; i++ ) {
  262                 if (is_sack_on_board(sf, &in[i])) {
  263 #ifndef _KERNEL
  264                         cnt_skipped_oldsack++;
  265 #endif
  266                         continue;
  267                 }
  268                 /* Did not find it (or found only
  269                  * a piece of it). Copy it to
  270                  * our outgoing board.
  271                  */
  272                 memcpy(&blkboard[num], &in[i], sizeof(struct sackblk));
  273 #ifndef _KERNEL
  274                 cnt_used_oldsack++;
  275 #endif
  276                 num++;
  277         }
  278         if (num) {
  279                 memcpy(in, blkboard, (num * sizeof(struct sackblk)));
  280         }
  281         return (num);
  282 }
  283 
  284 /*
  285  * Given idx its used but there is space available
  286  * move the entry to the next free slot
  287  */
  288 static void
  289 sack_move_to_empty(struct sack_filter *sf, uint32_t idx)
  290 {
  291         int32_t i, cnt;
  292 
  293         i = (idx + 1) % SACK_FILTER_BLOCKS;
  294         for (cnt=0; cnt <(SACK_FILTER_BLOCKS-1); cnt++) {
  295                 if (sack_blk_used(sf, i) == 0) {
  296                         memcpy(&sf->sf_blks[i], &sf->sf_blks[idx], sizeof(struct sackblk));
  297                         sf->sf_bits = sack_blk_clr(sf, idx);
  298                         sf->sf_bits = sack_blk_set(sf, i);
  299                         return;
  300                 }
  301                 i++;
  302                 i %= SACK_FILTER_BLOCKS;
  303         }
  304 }
  305 
  306 static int32_t
  307 sack_filter_new(struct sack_filter *sf, struct sackblk *in, int numblks, tcp_seq th_ack)
  308 {
  309         struct sackblk blkboard[TCP_MAX_SACK];
  310         int32_t num, i;
  311         /*
  312          * First lets trim the old and possibly
  313          * throw any away we have.
  314          */
  315         for(i=0, num=0; i<numblks; i++) {
  316                 if (is_sack_on_board(sf, &in[i]))
  317                         continue;
  318                 memcpy(&blkboard[num], &in[i], sizeof(struct sackblk));
  319                 num++;
  320         }
  321         if (num == 0)
  322                 return(num);
  323 
  324         /* Now what we are left with is either
  325          * completely merged on to the board
  326          * from the above steps, or is new
  327          * and need to be added to the board
  328          * with the last one updated to current.
  329          *
  330          * First copy it out, we want to return that
  331          * to our caller for processing.
  332          */
  333         memcpy(in, blkboard, (num * sizeof(struct sackblk)));
  334         numblks = num;
  335         /* Now go through and add to our board as needed */
  336         for(i=(num-1); i>=0; i--) {
  337                 if (is_sack_on_board(sf, &blkboard[i])) {
  338                         continue;
  339                 }
  340                 /* Add this guy its not listed */
  341                 sf->sf_cur++;
  342                 sf->sf_cur %= SACK_FILTER_BLOCKS;
  343                 if ((sack_blk_used(sf, sf->sf_cur)) &&
  344                     (sf->sf_used < SACK_FILTER_BLOCKS)) {
  345                         sack_move_to_empty(sf, sf->sf_cur);
  346                 }
  347 #ifndef _KERNEL
  348                 if (sack_blk_used(sf, sf->sf_cur)) {
  349                         over_written++;
  350                         if (sf->sf_used < SACK_FILTER_BLOCKS)
  351                                 empty_avail++;
  352                 }
  353 #endif
  354                 memcpy(&sf->sf_blks[sf->sf_cur], &in[i], sizeof(struct sackblk));
  355                 if (sack_blk_used(sf, sf->sf_cur) == 0) {
  356                         sf->sf_used++;
  357 #ifndef _KERNEL
  358                         if (sf->sf_used > highest_used)
  359                                 highest_used = sf->sf_used;
  360 #endif
  361                         sf->sf_bits = sack_blk_set(sf, sf->sf_cur);
  362                 }
  363         }
  364         return(numblks);
  365 }
  366 
  367 /*
  368  * Given a sack block on the board (the skip index) see if
  369  * any other used entries overlap or meet, if so return the index.
  370  */
  371 static int32_t
  372 sack_blocks_overlap_or_meet(struct sack_filter *sf, struct sackblk *sb, uint32_t skip)
  373 {
  374         int32_t i;
  375 
  376         for(i=0; i<SACK_FILTER_BLOCKS; i++) {
  377                 if (sack_blk_used(sf, i) == 0)
  378                         continue;
  379                 if (i == skip)
  380                         continue;
  381                 if (SEQ_GEQ(sf->sf_blks[i].end, sb->start) &&
  382                     SEQ_LEQ(sf->sf_blks[i].end, sb->end) &&
  383                     SEQ_LEQ(sf->sf_blks[i].start, sb->start)) {
  384                         /**
  385                          * The two board blocks meet:
  386                          *
  387                          *  board1   |--------|
  388                          *  board2       |----------|
  389                          *    <or>
  390                          *  board1   |--------|
  391                          *  board2   |--------------|
  392                          *    <or>
  393                          *  board1   |--------|
  394                          *  board2   |--------|
  395                          */
  396                         return(i);
  397                 }
  398                 if (SEQ_LEQ(sf->sf_blks[i].start, sb->end) &&
  399                     SEQ_GEQ(sf->sf_blks[i].start, sb->start) &&
  400                     SEQ_GEQ(sf->sf_blks[i].end, sb->end)) {
  401                         /**
  402                          * The board block partial meets:
  403                          *
  404                          *  board       |--------|
  405                          *  sack  |----------|
  406                          *     <or>
  407                          *  board       |----|
  408                          *  sack  |----------|
  409                          * 1) Update the board block to the new start
  410                          *      and
  411                          * 2) Update the start of this block to my end.
  412                          */
  413                         return(i);
  414                 }
  415         }
  416         return (-1);
  417 }
  418 
  419 /*
  420  * Collapse entry src into entry into
  421  * and free up the src entry afterwards.
  422  */
  423 static void
  424 sack_collapse(struct sack_filter *sf, int32_t src, int32_t into)
  425 {
  426         if (SEQ_LT(sf->sf_blks[src].start, sf->sf_blks[into].start)) {
  427                 /* src has a lower starting point */
  428                 sf->sf_blks[into].start = sf->sf_blks[src].start;
  429         }
  430         if (SEQ_GT(sf->sf_blks[src].end, sf->sf_blks[into].end)) {
  431                 /* src has a higher ending point */
  432                 sf->sf_blks[into].end = sf->sf_blks[src].end;
  433         }
  434         sf->sf_bits = sack_blk_clr(sf, src);
  435         sf->sf_used--;
  436 }
  437 
  438 static void
  439 sack_board_collapse(struct sack_filter *sf)
  440 {
  441         int32_t i, j, i_d, j_d;
  442 
  443         for(i=0; i<SACK_FILTER_BLOCKS; i++) {
  444                 if (sack_blk_used(sf, i) == 0)
  445                         continue;
  446                 /*
  447                  * Look at all other blocks but this guy
  448                  * to see if they overlap. If so we collapse
  449                  * the two blocks together.
  450                  */
  451                 j = sack_blocks_overlap_or_meet(sf, &sf->sf_blks[i], i);
  452                 if (j == -1) {
  453                         /* No overlap */
  454                         continue;
  455                 }
  456                 /*
  457                  * Ok j and i overlap with each other, collapse the
  458                  * one out furthest away from the current position.
  459                  */
  460                 if (sf->sf_cur > i)
  461                         i_d = sf->sf_cur - i;
  462                 else
  463                         i_d = i - sf->sf_cur;
  464                 if (sf->sf_cur > j)
  465                         j_d = sf->sf_cur - j;
  466                 else
  467                         j_d = j - sf->sf_cur;
  468                 if (j_d > i_d) {
  469                         sack_collapse(sf, j, i);
  470                 } else
  471                         sack_collapse(sf, i, j);
  472         }
  473 }
  474 
  475 #ifndef _KERNEL
  476 uint64_t saved=0;
  477 uint64_t tot_sack_blks=0;
  478 
  479 static void
  480 sack_filter_dump(FILE *out, struct sack_filter *sf)
  481 {
  482         int i;
  483         fprintf(out, "  sf_ack:%u sf_bits:0x%x c:%d used:%d\n",
  484                 sf->sf_ack, sf->sf_bits,
  485                 sf->sf_cur, sf->sf_used);
  486 
  487         for(i=0; i<SACK_FILTER_BLOCKS; i++) {
  488                 if (sack_blk_used(sf, i)) {
  489                         fprintf(out, "Entry:%d start:%u end:%u\n", i,
  490                                sf->sf_blks[i].start,
  491                                sf->sf_blks[i].end);
  492                 }
  493         }
  494 }
  495 #endif
  496 
  497 #ifndef _KERNEL
  498 static
  499 #endif
  500 int
  501 sack_filter_blks(struct sack_filter *sf, struct sackblk *in, int numblks,
  502                  tcp_seq th_ack)
  503 {
  504         int32_t i, ret;
  505 
  506         if (numblks > TCP_MAX_SACK) {
  507 #ifdef _KERNEL
  508                 panic("sf:%p sb:%p Impossible number of sack blocks %d > 4\n",
  509                       sf, in,
  510                       numblks);
  511 #endif
  512                 return(numblks);
  513         }
  514 #ifndef _KERNEL
  515         if ((sf->sf_used > 1) && (no_collapse == 0))
  516                 sack_board_collapse(sf);
  517 
  518 #else
  519         if (sf->sf_used > 1)
  520                 sack_board_collapse(sf);
  521 #endif
  522         if ((sf->sf_used == 0) && numblks) {
  523                 /*
  524                  * We are brand new add the blocks in
  525                  * reverse order. Note we can see more
  526                  * than one in new, since ack's could be lost.
  527                  */
  528                 int cnt_added = 0;
  529 
  530                 sf->sf_ack = th_ack;
  531                 for(i=(numblks-1), sf->sf_cur=0; i >= 0; i--) {
  532                         memcpy(&sf->sf_blks[sf->sf_cur], &in[i], sizeof(struct sackblk));
  533                         sf->sf_bits = sack_blk_set(sf, sf->sf_cur);
  534                         sf->sf_cur++;
  535                         sf->sf_cur %= SACK_FILTER_BLOCKS;
  536                         sf->sf_used++;
  537                         cnt_added++;
  538 #ifndef _KERNEL
  539                         if (sf->sf_used > highest_used)
  540                                 highest_used = sf->sf_used;
  541 #endif
  542                 }
  543                 if (sf->sf_cur)
  544                         sf->sf_cur--;
  545 
  546                 return (cnt_added);
  547         }
  548         if (SEQ_GT(th_ack, sf->sf_ack)) {
  549                 sack_filter_prune(sf, th_ack);
  550         }
  551         if (numblks) {
  552                 if (SEQ_GEQ(th_ack, sf->sf_ack)) {
  553                         ret = sack_filter_new(sf, in, numblks, th_ack);
  554                 } else {
  555                         ret = sack_filter_old(sf, in, numblks);
  556                 }
  557         } else
  558                 ret = 0;
  559         return (ret);
  560 }
  561 
  562 void
  563 sack_filter_reject(struct sack_filter *sf, struct sackblk *in)
  564 {
  565         /*
  566          * Given a specified block (that had made
  567          * it past the sack filter). Reject that
  568          * block triming it off any sack-filter block
  569          * that has it. Usually because the block was
  570          * too small and did not cover a whole send.
  571          *
  572          * This function will only "undo" sack-blocks
  573          * that are fresh and touch the edges of
  574          * blocks in our filter.
  575          */
  576         int i;
  577 
  578         for(i=0; i<SACK_FILTER_BLOCKS; i++) {
  579                 if (sack_blk_used(sf, i) == 0)
  580                         continue;
  581                 /*
  582                  * Now given the sack-filter block does it touch
  583                  * with one of the ends
  584                  */
  585                 if (sf->sf_blks[i].end == in->end) {
  586                         /* The end moves back to start */
  587                         if (SEQ_GT(in->start, sf->sf_blks[i].start))
  588                                 /* in-blk       |----| */
  589                                 /* sf-blk  |---------| */
  590                                 sf->sf_blks[i].end = in->start;
  591                         else {
  592                                 /* It consumes this block */
  593                                 /* in-blk  |---------| */
  594                                 /* sf-blk     |------| */
  595                                 /* <or> */
  596                                 /* sf-blk  |---------| */
  597                                 sf->sf_bits = sack_blk_clr(sf, i);
  598                                 sf->sf_used--;
  599                         }
  600                         continue;
  601                 }
  602                 if (sf->sf_blks[i].start == in->start) {
  603                         if (SEQ_LT(in->end, sf->sf_blks[i].end)) {
  604                                 /* in-blk  |----|      */
  605                                 /* sf-blk  |---------| */
  606                                 sf->sf_blks[i].start = in->end;
  607                         } else {
  608                                 /* It consumes this block */
  609                                 /* in-blk  |----------|  */
  610                                 /* sf-blk  |-------|     */
  611                                 /* <or> */
  612                                 /* sf-blk  |----------|  */
  613                                 sf->sf_bits = sack_blk_clr(sf, i);
  614                                 sf->sf_used--;
  615                         }
  616                         continue;
  617                 }
  618         }
  619 }
  620 
  621 #ifndef _KERNEL
  622 
  623 int
  624 main(int argc, char **argv)
  625 {
  626         char buffer[512];
  627         struct sackblk blks[TCP_MAX_SACK];
  628         FILE *err;
  629         tcp_seq th_ack, snd_una, snd_max = 0;
  630         struct sack_filter sf;
  631         int32_t numblks,i;
  632         int snd_una_set=0;
  633         double a, b, c;
  634         int invalid_sack_print = 0;
  635         uint32_t chg_remembered=0;
  636         uint32_t sack_chg=0;
  637         char line_buf[10][256];
  638         int line_buf_at=0;
  639 
  640         in = stdin;
  641         out = stdout;
  642         while ((i = getopt(argc, argv, "ndIi:o:?h")) != -1) {
  643                 switch (i) {
  644                 case 'n':
  645                         no_collapse = 1;
  646                         break;
  647                 case 'd':
  648                         detailed_dump = 1;
  649                         break;
  650                 case'I':
  651                         invalid_sack_print = 1;
  652                         break;
  653                 case 'i':
  654                         in = fopen(optarg, "r");
  655                         if (in == NULL) {
  656                                 fprintf(stderr, "Fatal error can't open %s for input\n", optarg);
  657                                 exit(-1);
  658                         }
  659                         break;
  660                 case 'o':
  661                         out = fopen(optarg, "w");
  662                         if (out == NULL) {
  663                                 fprintf(stderr, "Fatal error can't open %s for output\n", optarg);
  664                                 exit(-1);
  665                         }
  666                         break;
  667                 default:
  668                 case '?':
  669                 case 'h':
  670                         fprintf(stderr, "Use %s [ -i infile -o outfile -I]\n", argv[0]);
  671                         return(0);
  672                         break;
  673                 };
  674         }
  675         sack_filter_clear(&sf, 0);
  676         memset(buffer, 0, sizeof(buffer));
  677         memset(blks, 0, sizeof(blks));
  678         numblks = 0;
  679         fprintf(out, "************************************\n");
  680         while (fgets(buffer, sizeof(buffer), in) != NULL) {
  681                 sprintf(line_buf[line_buf_at], "%s", buffer);
  682                 line_buf_at++;
  683                 if (strncmp(buffer, "QUIT", 4) == 0) {
  684                         break;
  685                 } else if (strncmp(buffer, "DUMP", 4) == 0) {
  686                         sack_filter_dump(out, &sf);
  687                 } else if (strncmp(buffer, "MAX:", 4) == 0) {
  688                         snd_max = strtoul(&buffer[4], NULL, 0);
  689                 } else if (strncmp(buffer, "COMMIT", 6) == 0) {
  690                         int nn, ii;
  691                         if (numblks) {
  692                                 uint32_t szof, tot_chg;
  693                                 for(ii=0; ii<line_buf_at; ii++) {
  694                                         fprintf(out, "%s", line_buf[ii]);
  695                                 }
  696                                 fprintf(out, "------------------------------------\n");
  697                                 nn = sack_filter_blks(&sf, blks, numblks, th_ack);
  698                                 saved += numblks - nn;
  699                                 tot_sack_blks += numblks;
  700                                 fprintf(out, "ACK:%u\n", sf.sf_ack);
  701                                 for(ii=0, tot_chg=0; ii<nn; ii++) {
  702                                         szof = blks[ii].end - blks[ii].start;
  703                                         tot_chg += szof;
  704                                         fprintf(out, "SACK:%u:%u [%u]\n",
  705                                                blks[ii].start,
  706                                                 blks[ii].end, szof);
  707                                 }
  708                                 fprintf(out,"************************************\n");
  709                                 chg_remembered = tot_chg;
  710                                 if (detailed_dump) {
  711                                         sack_filter_dump(out, &sf);
  712                                         fprintf(out,"************************************\n");
  713                                 }
  714                         }
  715                         memset(blks, 0, sizeof(blks));
  716                         memset(line_buf, 0, sizeof(line_buf));
  717                         line_buf_at=0;
  718                         numblks = 0;
  719                 } else if (strncmp(buffer, "CHG:", 4) == 0) {
  720                         sack_chg = strtoul(&buffer[4], NULL, 0);
  721                         if ((sack_chg != chg_remembered) &&
  722                             (sack_chg > chg_remembered)){
  723                                 fprintf(out,"***WARNING WILL RODGERS DANGER!! sack_chg:%u last:%u\n",
  724                                         sack_chg, chg_remembered
  725                                         );
  726                         }
  727                         sack_chg = chg_remembered = 0;
  728                 } else if (strncmp(buffer, "RXT", 3) == 0) {
  729                         sack_filter_clear(&sf, snd_una);
  730                 } else if (strncmp(buffer, "ACK:", 4) == 0) {
  731                         th_ack = strtoul(&buffer[4], NULL, 0);
  732                         if (snd_una_set == 0) {
  733                                 snd_una = th_ack;
  734                                 snd_una_set = 1;
  735                         } else if (SEQ_GT(th_ack, snd_una)) {
  736                                 snd_una = th_ack;
  737                         }
  738                 } else if (strncmp(buffer, "EXIT", 4) == 0) {
  739                         sack_filter_clear(&sf, snd_una);
  740                         sack_chg = chg_remembered = 0;
  741                 } else if (strncmp(buffer, "SACK:", 5) == 0) {
  742                         char *end=NULL;
  743                         uint32_t start;
  744                         uint32_t endv;
  745 
  746                         start = strtoul(&buffer[5], &end, 0);
  747                         if (end) {
  748                                 endv = strtoul(&end[1], NULL, 0);
  749                         } else {
  750                                 fprintf(out, "--Sack invalid skip 0 start:%u : ??\n", start);
  751                                 continue;
  752                         }
  753                         if (SEQ_GT(endv, snd_max))
  754                                 snd_max = endv;
  755                         if (SEQ_LT(endv, start)) {
  756                                 fprintf(out, "--Sack invalid skip 1 endv:%u < start:%u\n", endv, start);
  757                                 continue;
  758                         }
  759                         if (numblks == TCP_MAX_SACK) {
  760                                 fprintf(out, "--Exceeded max %d\n", numblks);
  761                                 exit(0);
  762                         }
  763                         blks[numblks].start = start;
  764                         blks[numblks].end = endv;
  765                         numblks++;
  766                 } else if (strncmp(buffer, "REJ:n:n", 4) == 0) {
  767                         struct sackblk in;
  768                         char *end=NULL;
  769 
  770                         in.start = strtoul(&buffer[4], &end, 0);
  771                         if (end) {
  772                                 in.end = strtoul(&end[1], NULL, 0);
  773                                 sack_filter_reject(&sf, &in);
  774                         } else
  775                                 fprintf(out, "Invalid input END:A:B\n");
  776                 } else if (strncmp(buffer, "HELP", 4) == 0) {
  777                         fprintf(out, "You can input:\n");
  778                         fprintf(out, "SACK:S:E -- to define a sack block\n");
  779                         fprintf(out, "RXT -- to clear the filter without changing the remembered\n");
  780                         fprintf(out, "EXIT -- To clear the sack filter and start all fresh\n");
  781                         fprintf(out, "ACK:N -- To advance the cum-ack to N\n");
  782                         fprintf(out, "MAX:N -- To set send-max to N\n");
  783                         fprintf(out, "COMMIT -- To apply the sack you built to the filter and dump the filter\n");
  784                         fprintf(out, "DUMP -- To display the current contents of the sack filter\n");
  785                         fprintf(out, "QUIT -- To exit this program\n");
  786                 } else {
  787                         fprintf(out, "Command %s unknown\n", buffer);
  788                 }
  789                 memset(buffer, 0, sizeof(buffer));
  790         }
  791         if (in != stdin) {
  792                 fclose(in);
  793         }
  794         if (out != stdout) {
  795                 fclose(out);
  796         }
  797         a = saved * 100.0;
  798         b = tot_sack_blks * 1.0;
  799         if (b > 0.0)
  800                 c = a/b;
  801         else
  802                 c = 0.0;
  803         if (out != stdout)
  804                 err = stdout;
  805         else
  806                 err = stderr;
  807         fprintf(err, "Saved %lu sack blocks out of %lu (%2.3f%%) old_skip:%lu old_usd:%lu high_cnt:%d ow:%d ea:%d\n",
  808                 saved, tot_sack_blks, c, cnt_skipped_oldsack, cnt_used_oldsack, highest_used, over_written, empty_avail);
  809         return(0);
  810 }
  811 #endif

Cache object: 38aeb4a4fd776b6f08dbab69f209d136


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.