📄 tcpsend.c
字号:
/* -- how is the socket configured vis-a-vis socket options *//* -- Whether this is a SYN segment or a data segment *//* Size of options is the return function value, and also returns the */ /* options this size is based on in which_options */export int tcp_seg_optsize(fast tcpsv_t *svp, int synseg, u16 *which_options){ int optsize = 0; /* Find out which options we should send, based on the configuration of this socket and/or what options we received in an incoming SYN */ *which_options = tcp_seg_which_options(svp,synseg); /* See if MSS option is needed. */ if (*which_options & TCP_OPT_INCLUDE_MSS) optsize += TCPO_MSS_LEN;#ifdef TCP_TIMESTAMPS /* See if the timestamps option is needed. If so, we want to align it so that the timestamp fields are aligned on 4-byte-multiple offsets from the beginning of the options, as recommended in Appendix A of RFC 1323. So, add enough to our accumulating size to account for that alignment */ if (*which_options & TCP_OPT_INCLUDE_TIMESTAMPS) { int op2mod4 = ( (optsize + 2) % 4); if (op2mod4) optsize += ( 4 - op2mod4); optsize += TCPO_TMSTMP_LEN; } /* if */#endif /* TCP_TIMESTAMPS */#ifdef TCP_WND_SCALE /* See if Window scale option is needed. */ if (*which_options & TCP_OPT_INCLUDE_WSF) optsize += TCPO_WSF_LEN;#endif /* TCP_WND_SCALE */#ifdef TCP_SELACK_ENHANCEMENTS /* See if the SACK_PERMITTED option is needed */ if (*which_options & TCP_OPT_INCLUDE_SACK_PERMITTED) optsize += TCPO_SACK_PERMITTED_LEN;#endif#ifdef TCP_TRANSACTION_TCP if (*which_options & TCP_OPT_INCLUDE_CC) optsize += TCPO_CC_LEN; if (*which_options & TCP_OPT_INCLUDE_CCNEW) optsize += TCPO_CCNEW_LEN; if (*which_options & TCP_OPT_INCLUDE_CCECHO) optsize += TCPO_CCECHO_LEN;#endif /* TCP_TRANSACTION_TCP */ /* If the accumulated length is not a multiple of 4, round it up to such a multiple. */ if ( (optsize % 4) != 0 ) optsize += (4 - (optsize % 4) ); return(optsize);} /* tcp_seg_optsize *//**************************************************************************//* Function to construct the options field of a TCP segment, given the *//* set of options passed in the parameter which_options. The options are *//* to be constructed in the buffer pointed to by optr, of length olen. *//* The buffer is to be padded with NOPS if it is longer than needed to *//* contain the specified options. If it is not long enough, we have a bug.*/export void tcp_construct_seg_options(fast tcpsv_t *svp, char *optr, int olen, u16 which_options){ fast TCPO_T *options = (TCPO_T *) optr; /* IMPORTANT: THE ORDER IN WHICH OPTIONS ARE CONSTRUCTED HERE MUST BE THE SAME AS THAT WHICH THEY WERE CONSIDERED IN "tcp_seg_optsize", AND ALL LOGIC CONCERNING ALIGNMENT AND PADDING MUST BE IDENTICAL HERE TO THAT WHICH WAS USED THERE. */ if (which_options & TCP_OPT_INCLUDE_MSS) { assert( ((u32)(options + TCPO_MSS_LEN) <= (u32)(optr + olen) ), "tcp mss option overflow"); options[TCPO_KIND] = TCPO_MSS_KIND; options[TCPO_SIZE] = TCPO_MSS_LEN; /* the value of this option will be 'dink'ed later */ options += TCPO_MSS_LEN; } /* if */#ifdef TCP_TIMESTAMPS /* Pad with NOPs if necessary to achieve the preferred alignment. See comment in "tcp_seg_optsize" about that alignment */ if (which_options & TCP_OPT_INCLUDE_TIMESTAMPS) { while ( ((((char *) options) - optr) % 4) != 2) { *options++ = TCPO_NOP_KIND; } /* while */ assert( ((u32)(options + TCPO_TMSTMP_LEN) <= (u32)(optr + olen) ), "tcp timestamp option overflow"); options[TCPO_KIND] = TCPO_TMSTMP_KIND; options[TCPO_SIZE] = TCPO_TMSTMP_LEN; HostToNet32(&(options[TCPO_TMSTMP_TSVAL]),TCP_CURRENT_HRES_TIME ); /* Per RFC 1323, the following fields must be zero if the ACK bit in the outgoing segment is not set. I'll need to come back and deal with that later */ HostToNet32(&(options[TCPO_TMSTMP_TSECR]),svp->sv_recent_rx_tmstmp); options += TCPO_TMSTMP_LEN; }#endif /* TCP_TIMESTAMPS */#ifdef TCP_WND_SCALE if (which_options & TCP_OPT_INCLUDE_WSF) { assert( ((u32)(options + TCPO_WSF_LEN) <= (u32)(optr + olen) ), "tcp wsf option overflow"); options[TCPO_KIND] = TCPO_WSF_KIND; options[TCPO_SIZE] = TCPO_WSF_LEN; options[TCPO_VAL] = (u8) tcp_rcv_wscale_value(svp); options += TCPO_WSF_LEN; } /* if */#endif /* TCP_WND_SCALE */#ifdef TCP_SELACK_ENHANCEMENTS if (which_options & TCP_OPT_INCLUDE_SACK_PERMITTED) { assert( ((options + TCPO_SACK_PERMITTED_LEN) <= (optr + olen) ), "tcp sack permitted option overflow"); options[TCPO_KIND] = TCPO_SACK_PERMITTED_KIND; options[TCPO_SIZE] = TCPO_SACK_PERMITTED_LEN; options += TCPO_SACK_PERMITTED_LEN; }#endif /* TCP_SELACK_ENHANCEMENTS */#ifdef TCP_TRANSACTION_TCP if (which_options & TCP_OPT_INCLUDE_CC) { assert( ((options + TCPO_CC_LEN) <= (optr + olen) ), "tcp CC option overflow"); options[TCPO_KIND] = TCPO_CC_KIND; options[TCPO_SIZE] = TCPO_CC_LEN; HostToNet32(&(options[TCPO_CC_VAL]),svp->sv_ccsend ); options += TCPO_CC_LEN; } if (which_options & TCP_OPT_INCLUDE_CCNEW) { assert( ((options + TCPO_CCNEW_LEN) <= (optr + olen) ), "tcp CCNEW option overflow"); options[TCPO_KIND] = TCPO_CCNEW_KIND; options[TCPO_SIZE] = TCPO_CCNEW_LEN; HostToNet32(&(options[TCPO_CCNEW_VAL]),svp->sv_ccsend ); options += TCPO_CCNEW_LEN; } if (which_options & TCP_OPT_INCLUDE_CCECHO) { assert( ((options + TCPO_CCECHO_LEN) <= (optr + olen) ), "tcp CCECHO option overflow"); options[TCPO_KIND] = TCPO_CCECHO_KIND; options[TCPO_SIZE] = TCPO_CCECHO_LEN; HostToNet32(&(options[TCPO_CCECHO_VAL]),svp->sv_ccrecv ); options += TCPO_CCECHO_LEN; }#endif /* TCP_TRANSACTION_TCP */#ifdef TCP_SELACK_ENHANCEMENTS if (which_options & TCP_OPT_INCLUDE_SACK) { int sack_block_count; int sbi; /* How many do we have room for? */ sack_block_count = tcp_num_sack_blocks_room_for( (optr + olen) - ((char *) options) ); /* Adjust count downward if we have fewer SACK blocks than that */ if (svp->sv_num_out_sack_blocks < sack_block_count) { sack_block_count = svp->sv_num_out_sack_blocks; } /* if */ /* Put the option header in */ options[TCPO_KIND] = TCPO_SACK_KIND; options[TCPO_SIZE] = ( TCPO_VAL + (TCPO_SACK_BLOCK_SIZE * sack_block_count) ); options += TCPO_VAL; /* Copy the number of SACK blocks into the header that we have decided we need or have room for. Start from the most-redently added SACK block */ sbi = (MAX_NUM_OUT_SACK_BLOCKS - svp->sv_num_out_sack_blocks); while (sack_block_count > 0 ) { tcp_sack_block_ *sbp = &(svp->sv_out_sack_blocks[sbi]); /* The SACK blocks are stored in network order so no need for conversion here */ *((u32 *) options) = sbp->begin_seqno; *((u32 *) (options + sizeof(u32) ) ) = sbp->end_seqno; options += TCPO_SACK_BLOCK_SIZE; sack_block_count--; sbi++; } /* while */ }#endif /* TCP_SELACK_ENHANCEMENTS */ /* If we are not on a 4-byte boundary, put in an end-of-list option */ if ( ( ( (char *) options - optr) % 4 ) != 0) options[TCPO_KIND] = TCPO_EOL_KIND;} /* tcp_construct_seg_options *//***************************************************************************//* Function to find a specified option within the options field of a TCP *//* segment header. Returns a pointer to the specified option or NULL if its*//* not present. */export TCPO_T *tcp_find_option(TCPH_T *tcphp, u8 kind){ TCPO_T *opptr = (TCPO_T *) TCPHOPTPTR(tcphp); TCPO_T *endopptr = (TCPO_T *) ( opptr + TCPHOPTL(tcphp) ); /* loop thru the options until we reach the end of the options */ while (opptr < endopptr) { /* Have we found the one we're looking for? */ if (*opptr == kind) return(opptr); /* Is this the end of the option list? */ if (*opptr == TCPO_EOL_KIND) break; /* Bump to the next option */ switch (*opptr) { /* Options with only a "kind" field */ case TCPO_NOP_KIND: opptr++;break; /* Option with a "kind" and a "length" field */ default: opptr += opptr[TCPO_SIZE]; break; } /* switch */ } /* for */ /* If down here we did not find the speecified option type */ return((TCPO_T *) 0);} /* tcp_find_option *//***************************************************************************//* Send queue transmission * Retransmission policy only sends out the first unacknowledged packet */export void tcp_sqxmit (fast tcpsv_t *svp){ fast m * mp = (m *)0, * nmp; fast i32 i1, nsend; fast u32 seqno; boolean finflg, synflg; boolean retransmitting;#ifdef FAST_REXMT_DEBUG int entered_with_rip_flag_set = 0; int iter_ctr = 0;#endif boolean loop_again = true; char * dp = (char *)0; so_t * sop;#ifdef TRACE_SQXMIT int local_inv_ctr;#endif use_critical; trace0(tcp_trace, "tcp_sqxmit:\n"); /* if another instance of this routine is running, we're done */ if (svp->sv_flags & SV_SENDING) { return; } nmp = (m *)0; /* for testing first-time through calculations */#ifdef TRACE_SQXMIT critical; inv_ctr++; local_inv_ctr = inv_ctr; normal;#endif svp->sv_flags &= ~SV_UPSEND_PENDING;#ifdef FAST_REXMT_DEBUG if (svp->sv_flags & SV_RIP) { os_printf("\n(%d) tcp_sqxmit: entering with RIP flag set, suna=%u\n", t_time, MU32(svp->sv_suna)); entered_with_rip_flag_set = 1; }#endiftop:; critical; sop = sv_valid_sop(svp,sop); /* validity of sop will be checked in various places below as necessary */#ifdef FAST_REXMT_DEBUG if (entered_with_rip_flag_set) { os_printf("\n(%d) (i=%d) at top, suna=%u\n", t_time,++iter_ctr,MU32(svp->sv_suna)); }#endif /* turn off sending condition; eases the many returns below */ svp->sv_flags &= ~SV_SENDING; assert(svp->sv_refcnt != 0, "tcp_sqxmit: sv not bound\n"); if ( svp->sv_state == CLOSED || (sop == (so_t *)0) ) { normal; return; } /* if */ assert(MC32M(svp->sv_suna, <=, svp->sv_surg), "tcp_sqxmit: surg too small\n"); assert(MC32M(svp->sv_surg, <=, svp->sv_snxt), "tcp_sqxmit: surg too big\n"); assert(MC32M(svp->sv_suna, <=, svp->sv_spsh), "tcp_sqxmit: spsh too small\n"); assert(MC32M(svp->sv_spsh, <=, svp->sv_snxt), "tcp_sqxmit: spsh too big\n"); trace3(tcp_tsqxmit, "tcp_sqxmit: suna 0x%x spsh 0x%x snxt 0x%x\n", MU32(svp->sv_suna), MU32(svp->sv_spsh), MU32(svp->sv_snxt)); /* Decide whether or not we want this interation to retransmit a segment or to send new data */#ifndef TCP_RETRANSMIT_ENHANCEMENTS retransmitting = ( (svp->sv_flags & SV_RIP) != 0);#else retransmitting = ( ((svp->sv_flags & SV_RIP) != 0) && (svp->sv_retr_window > 0) );#endif /******************************************************/ /* Determine the sequence # of first byte to be sent, */ /* and the amount of data to send in this segment */ /******************************************************/ if (retransmitting) { /* retransmission */ /* retransmit 1 segment from unacknowledged pointer */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -