📄 tcpsock.c
字号:
while others don't allow connections. We choose to treat it as a one rather than return an error. */ backlog = 1; } if (svp->sv_state != CLOSED) { err = FNS_EISCONN; goto out; } gq_max_set(&sop->so_cq, (int)min(backlog, (int)max_backlog)); q_init((q *)&sop->so_q, F_Q_HEADER); /* accept sockets */ tcp_no_more_data_in(svp); set_state("tcp_listen", svp, LISTEN); svp->sv_flags |= SV_PASSIVE; SET_SOCKET_OPTION_ON(sop,SO_ACCEPTCONN);out: sv_detach(svp, tcp_listen, true); return err;} /* tcp_listen *//* Function called by tcp_recv to determine whether a window update should be sent, and if so, whether it should be delayed or immediate *//* returns one of the following values */#define TCP_SEND_NO_WIND_UPDATE 0#define TCP_SEND_DELAYED_WIND_UPDATE 1#define TCP_SEND_IMMEDIATE_WIND_UPDATE 2static int tcp_decide_window_update(fast so_t * sop, fast tcpsv_t * svp, int length){ u32 rwind, whalf; /* Acknowledgements under full-speed conditions come out of the * following logic. Simply, a delayed window update (acknowledgement) * was requested when the data was received and placed in the receive * queue. If things are operating at high speed though that * delayed ACK will be superceded by a forced window update * generated here, thus piggy-backing ACK's with window openings. * These window updates are offered at half-open conditions, or * when enough data has been 'recv'ed to constitute two maximum-sized * segments since the last window update (this is how we implement the * "ack every other segment" requirement of RFC 1122). One additional * consideration is, if the window is opening back up from having been * advertised as zero, an updated window is advertised once space is * available in the receive buffer for a single maximum-sized segment. */ rwind = rwnd(sop); whalf = (int)(sop->so_rq.gq_max >> 1); /* if the remote does better with fully-open window ACK's being * sent immediately (e.g. Nagle send policy), do it now */ if (rwind == sop->so_rq.gq_max && (svp->sv_flags & SV_EMPTYACK)) { return(TCP_SEND_IMMEDIATE_WIND_UPDATE); /* if we are rising back from a zero-window advertisement and we have not yet reached the "silly window" threshold (i.e., we would be advertising a very small window), don't send an ack now. But if we have risen back above the "silly window" threshold, do send one now */ } else if (svp->sv_flags & SV_RECOVZWIN) { u32 silly_thresh = ((svp->sv_mssd < whalf) ? svp->sv_mssd : whalf); return( (rwind < silly_thresh) ? TCP_SEND_NO_WIND_UPDATE : TCP_SEND_IMMEDIATE_WIND_UPDATE); /* if we've read N maximum-sized segements since the last window update (N being a generalization of the "ack every second segment" where N is a configurable parameter), offer a window update now (this is the "ack every other segment" strategy described by Stevens */ } else if (svp->sv_rcvsncwupd >= (svp->sv_ackEveryN * svp->sv_mssd) ) { return(TCP_SEND_IMMEDIATE_WIND_UPDATE); /* if the read just done has caused the amount of available space in the receive buffer to exceed half of the receive buffer size, offer a window now */ } else if (whalf >= svp->sv_mssd && rwind >= whalf && rwind-length < whalf) { return(TCP_SEND_IMMEDIATE_WIND_UPDATE); /* No need to offer an immediate update, one will be sent after a delay period, or, if additional data is read from the socket prior to that, we'll go thru this decision process again. */ } else { return(TCP_SEND_DELAYED_WIND_UPDATE); }} /* tcp_decide_window_update *//* ULP receive event */export inttcp_recv (fast so_t * sop, char * buf, int * buf_lenp, int flags){ fast tcpsv_t * svp = sop->so_svp; int err; auto int length; use_critical; trace0(tcp_trace, "tcp_recv:\n"); /***********************************************************************/ /* Code to deal with out-of-band reads */ /* Is this an attempt to read "out-of-band" data, i.e., an urgent byte? */ if (flags & MSG_OOB) { /* Possibly we don't have a state vector -- if thats the case, its probably because the TCP connection was closed even though the application is still reading data out of the socket. In that case we've lost the urgent byte (possibly thats an argument for keeping it with the socket structure rather than the TCP state vector) */ if (svp == (tcpsv_t *) 0) return(FNS_ENOTCONN); /* not allowed if the OOBINLINE socket option is in effect */ if (svp->sv_urg_mode != SV_URGMODE_OOB) return(FNS_EINVAL); /* Well, we are in out-of-band mode -- lets see what state we are in right now with respect to urgent data */ switch (svp->sv_urg_state) { case SV_URGSTATE_NONE: /* MSG_OOB not allowed in this state */ return(FNS_EINVAL); case SV_URGSTATE_NODATA: /* tell user urgent byte not here yet */ return(FNS_EWOULDBLOCK); case SV_URGSTATE_DATA: /* return the urgent byte to the user and return to non-urgent state */ so_clr_notify(sop, URGENT_NOTIFY); svp->sv_urg_state = SV_URGSTATE_NONE; /* assuming that buf_lenp is at least one, not checking */ *buf = svp->sv_urg_buf; *buf_lenp = 1; return(FNS_ENOERR); } /* switch */ } /* end if out-of-band read */ /***********************************************************************/ err = so_rbcomm(sop, buf, buf_lenp, flags); /* if 'so_rbcomm' wasn't PEEKing, receive queue * will be locked upon return; 'gq_out' will unlock it */ debug2(tcp_debug && err, "tcp_recv: [%d] so_rbcomm returns err = %d\n", sop->so_index, err); if ((flags & MSG_PEEK) || err) { goto out; } /* If there is urgent data and we are receiving urgent data inline, a read operation must (if it reaches the urgent byte) stop at the byte before (Stevens Unix Network Programming page 574) so that the application can check atmark. But if they are reading at the mark right now, let them read it */ if ( (svp->sv_urg_state == SV_URGSTATE_DATA) && (svp->sv_urg_mode == SV_URGMODE_INLINE) ) { u32 read_seqno = MU32(svp->sv_read); u32 urg_off = (M32U(svp->sv_rurg, -, read_seqno) - 1); if ( (urg_off > 0) && (*buf_lenp > (int) urg_off) ) { *buf_lenp = (int)urg_off; } } length = *buf_lenp; trace1(tcp_trcv, "tcp_recv: accepted %d. bytes\n", length); gq_out(&sop->so_rq, length); /* check it out */ if ( length == 0 ) /* We called gq_out to unlock the receive queue, so now * we can return. */ goto out; /* The connection and the state vector could be long gone even though * we are still reading data out of the socket, so we don't assume * here that there has to still be a state vector. */ if ( svp == (tcpsv_t *)0 ) goto out; critical; sv_bind(svp, tcp_recv, true); /* keeping track of how many bytes read from socket since last time we sent an ACK */ svp->sv_rcvsncwupd += length; /* Determine whether or not we need to send a window update now, later, or not at all, and do it. */ switch (tcp_decide_window_update(sop,svp,length)) { case TCP_SEND_NO_WIND_UPDATE: break; case TCP_SEND_DELAYED_WIND_UPDATE: tcp_schedule_delayed_ack(svp,0); break; case TCP_SEND_IMMEDIATE_WIND_UPDATE: tcp_sndack((m *) 0, svp); break; } /* If we are in an urgent state (inline) and with this read we are reading past the the urgent data, clear the urgent state */ M32U(svp->sv_read, +=, length); if ( (svp->sv_urg_mode == SV_URGMODE_INLINE) && (svp->sv_urg_state != SV_URGSTATE_NONE) && MC32M(svp->sv_read, >=, svp->sv_rurg)) { /* Read past urgent pointer in URGENT mode */ so_clr_notify(sop, URGENT_NOTIFY); svp->sv_urg_state = SV_URGSTATE_NONE; /* Clear the URGENT condition */#if 0 os_printf("\ntcp_recv: reading past urgent data, clearing urgent state \n");#endif }#ifdef MSD_DEBUG { extern boolean msd_debug; if (msd_debug && length) os_printf("tcp_recv: length ==%d RFIN==%c rqcnt==%d\n", length, (svp->sv_flags & SV_RFINFLG)?'y':'n',sop->so_rq.gq_cnt); }#endif /* The application needs to be told when FIN arrives and * no data is waiting to be read. */ if ((svp->sv_flags & SV_RFINFLG) && sop->so_rq.gq_cnt == 0 && length) { normal; /* FIN seen and is now next to be read. */ so_notify(sop, RSHUTDOWN_NOTIFY); } else normal; sv_detach(svp, tcp_recv, true);out: return err;}/* reject a connection request */export int tcp_reject (fast so_t * sop, boolean can_wait){ fast so_t * nsop; use_critical; critical; if ( (nsop = get_accept_sop(sop)) == (so_t *)0 ) { normal; return FNS_EINVAL; } q_out(&nsop->so_q); nsop->so_flags &= ~F_SO_TEMP; /* don't let tcp_rslf() close the socket*/ tcp_cabort(nsop); so_smoke(nsop); so_ditchsop(nsop); if (q_empty(&sop->so_q)) so_clr_notify(sop, READ_NOTIFY|ACCEPT_NOTIFY); /* no more connection requests */ normal; return 0;} /* tcp_reject *//*********************************************************************************//* ULP transmit event */export int tcp_send (so_t *sop, char *buf, int *buf_lenp, int flags){ fast tcpsv_t *svp; auto int err; use_critical; trace0(tcp_trace, "tcp_send:\n"); critical; if ((svp = sop->so_svp) == (tcpsv_t *)0) { debug0(tcp_debug, "tcp_send: no state vector\n"); normal; return FNS_ENOTCONN; } sv_bind(svp, tcp_send, true); normal; trace1(tcp_tsend, "tcp_send: state = %s\n", tcp_st(svp->sv_state)); switch (svp->sv_state) { case CLOSED:#ifdef TCP_TRANSACTION_TCP if (svp->sv_t_tcp_flags & SV_TRANSACTION) { err = tcp_do_implied_connect(sop,svp,buf,buf_lenp,flags); goto out; }#endif /* TCP_TRANSACTION_TCP */ err = FNS_ENOTCONN; goto out; case ESTAB: case SYN_RECVD: case SYN_SENT: case CLOSE_WAIT: break; default: /* no sends allowed otherwise */ debug1(tcp_debug, "tcp_send: incorrect state: %d\n", svp->sv_state); err = svp->sv_err ? svp->sv_err : FNS_ENOTCONN; goto out; } err = 0; /* put data on send queue and send it. New version of so_sbcomm now takes care of calling tcp_sqxmit() so no need to do it here. */ so_sbcomm(sop, buf, buf_lenp, flags, (int *)&err);out:#ifdef TCP_TRANSACTION_TCP /* If we avoided sending a FIN before due to having reverted to non-transaction mode (in sob.c), do it now */ if ( (err == FNS_ENOERR) || (err == FNS_EINPROGRESS) ) { if ( (!svp->sv_t_tcp_flags & SV_TRANSACTION) && (flags & MSG_EOF) ) { tcp_sfin(svp, false); } }#endif /* TCP_TRANSACTION_TCP */ sv_detach(svp, tcp_send, true); return err;} /* tcp_send *//*************************************************************************************//* Processing for the SOL_SOCKET OOBINLINE option */static int tcp_setopt_oobinline (fast so_t *sop, int optname, char *optval){ tcpsv_t *svp; svp = sop->so_svp; if (!svp) return(FNS_ENOERR); /* maybe we should return an error here? */ /* But tcp_glomopt below doesn't even return an error if it can't transltate the sop into a svp, so if I want to worry about it here I should worry about it there too */ svp->sv_urg_mode = ( ((*((int *)optval)) != 0) ? SV_URGMODE_INLINE : SV_URGMODE_OOB ); return(FNS_ENOERR);} /* tcp_setopt_oobinline *//*************************************************************************************//* Processing for the SOL_SOCKET SNDBUF option */static int tcp_setopt_sndbuf (fast so_t *sop, int optname, char *optval){ tcpsv_t *svp; int sbs = *((int *)optval); svp = sop->so_svp; if (!svp) return(FNS_ENOTCONN); #ifndef TCP_WND_SCALE /* If window scaling is compile-time disabled, then don't allow values above 65535 (max rcv window size we can receive) */ if (sbs > TCP_MAX_WINDOW_FIELD_VALUE) return(FNS_EINVAL);#endif /* Store the new maximum senbuf value in TCP's control block */ svp->sv_sq_max = sbs; /* Don't store it in the send queue control block right now if to do so would interfere with protocol operations, i.e. recovery from a source quench where we have lowered the receive queue size and are gradually adjusting it back upward to its max. */ if ( (svp->sv_sqcntdwn != 0) && (svp->sv_sq_max >= sop->so_sq.gq_max) ) return (FNS_ENOERR); /* Either we are not in source quench recovery mode, or the specified new max is less than the current value of the receive queue, in which case we should abort the recovery operation anyway and make the new queue size operative */ svp->sv_sqcntdwn = 0; gq_max_set(&sop->so_sq, svp->sv_sq_max); return(FNS_ENOERR);} /* tcp_setopt_sndbuf *//*************************************************************************************//* Processing for the SOL_SOCKET RCV option */static int tcp_setopt_rcvbuf (fast so_t *sop, int optname, char *optval){ tcpsv_t *svp; int rbs = *((int *)optval); svp = sop->so_svp; if (!svp) return(FNS_ENOTCONN); /* Reject values above the maximum allowed. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -