tcp_usrreq.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 768 行 · 第 1/2 页

C
768
字号
#ifdef TCPDEBUG		req |= (int)nam << 8;		/* for debug's sake */#endif		break;	default:		panic("tcp_usrreq");	}#ifdef TCPDEBUG	if (tp && (so->so_options & SO_DEBUG))		tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);#endif	splx(s);	return (error);}/* * Common subroutine to open a TCP connection to remote host specified * by struct sockaddr_in in mbuf *nam.  Call in_pcbbind to assign a local * port number if needed.  Call in_pcbladdr to do the routing and to choose * a local host address (interface).  If there is an existing incarnation * of the same connection in TIME-WAIT state and if the remote host was * sending CC options and if the connection duration was < MSL, then * truncate the previous TIME-WAIT state and proceed. * Initialize connection parameters and enter SYN-SENT state. */inttcp_connect(tp, nam)	register struct tcpcb *tp;	struct mbuf *nam;{	struct inpcb *inp = tp->t_inpcb, *oinp;	struct socket *so = inp->inp_socket;	struct tcpcb *otp;	struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);	struct sockaddr_in *ifaddr;	int error;	struct rmxp_tao *taop;	struct rmxp_tao tao_noncached;	if (inp->inp_lport == 0) {		error = in_pcbbind(inp, NULL);		if (error)			return error;	}	/*	 * Cannot simply call in_pcbconnect, because there might be an	 * earlier incarnation of this same connection still in	 * TIME_WAIT state, creating an ADDRINUSE error.	 */	error = in_pcbladdr(inp, nam, &ifaddr);	if (error)		return error;	oinp = in_pcblookup(inp->inp_pcbinfo->listhead,	    sin->sin_addr, sin->sin_port,	    inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr						: ifaddr->sin_addr,	    inp->inp_lport,  0);	if (oinp) {		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&		otp->t_state == TCPS_TIME_WAIT &&		    otp->t_duration < TCPTV_MSL &&		    (otp->t_flags & TF_RCVD_CC))			otp = tcp_close(otp);		else			return EADDRINUSE;	}	if (inp->inp_laddr.s_addr == INADDR_ANY)		inp->inp_laddr = ifaddr->sin_addr;	inp->inp_faddr = sin->sin_addr;	inp->inp_fport = sin->sin_port;	in_pcbrehash(inp);	tp->t_template = tcp_template(tp);	if (tp->t_template == 0) {		in_pcbdisconnect(inp);		return ENOBUFS;	}	/* Compute window scaling to request.  */	while (tp->request_r_scale < TCP_MAX_WINSHIFT &&	    (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)		tp->request_r_scale++;	soisconnecting(so);	tcpstat.tcps_connattempt++;	tp->t_state = TCPS_SYN_SENT;	tp->t_timer[TCPT_KEEP] = tcp_keepinit;	tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;	tcp_sendseqinit(tp);	/*	 * Generate a CC value for this connection and	 * check whether CC or CCnew should be used.	 */	if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {		taop = &tao_noncached;		bzero(taop, sizeof(*taop));	}	tp->cc_send = CC_INC(tcp_ccgen);	if (taop->tao_ccsent != 0 &&	    CC_GEQ(tp->cc_send, taop->tao_ccsent)) {		taop->tao_ccsent = tp->cc_send;	} else {		taop->tao_ccsent = 0;		tp->t_flags |= TF_SENDCCNEW;	}	return 0;}inttcp_ctloutput(op, so, level, optname, mp)	int op;	struct socket *so;	int level, optname;	struct mbuf **mp;{	int error = 0, s;	struct inpcb *inp;	register struct tcpcb *tp;	register struct mbuf *m;	register int i;	s = splnet();	inp = sotoinpcb(so);	if (inp == NULL) {		splx(s);		if (op == PRCO_SETOPT && *mp)			(void) m_free(*mp);		return (ECONNRESET);	}	if (level != IPPROTO_TCP) {		error = ip_ctloutput(op, so, level, optname, mp);		splx(s);		return (error);	}	tp = intotcpcb(inp);	switch (op) {	case PRCO_SETOPT:		m = *mp;		switch (optname) {		case TCP_NODELAY:			if (m == NULL || m->m_len < sizeof (int))				error = EINVAL;			else if (*mtod(m, int *))				tp->t_flags |= TF_NODELAY;			else				tp->t_flags &= ~TF_NODELAY;			break;		case TCP_MAXSEG:			if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)				tp->t_maxseg = i;			else				error = EINVAL;			break;		case TCP_NOOPT:			if (m == NULL || m->m_len < sizeof (int))				error = EINVAL;			else if (*mtod(m, int *))				tp->t_flags |= TF_NOOPT;			else				tp->t_flags &= ~TF_NOOPT;			break;		case TCP_NOPUSH:			if (m == NULL || m->m_len < sizeof (int))				error = EINVAL;			else if (*mtod(m, int *))				tp->t_flags |= TF_NOPUSH;			else				tp->t_flags &= ~TF_NOPUSH;			break;		default:			error = ENOPROTOOPT;			break;		}		if (m)			(void) m_free(m);		break;	case PRCO_GETOPT:		*mp = m = m_get(M_WAIT, MT_SOOPTS);		m->m_len = sizeof(int);		switch (optname) {		case TCP_NODELAY:			*mtod(m, int *) = tp->t_flags & TF_NODELAY;			break;		case TCP_MAXSEG:			*mtod(m, int *) = tp->t_maxseg;			break;		case TCP_NOOPT:			*mtod(m, int *) = tp->t_flags & TF_NOOPT;			break;		case TCP_NOPUSH:			*mtod(m, int *) = tp->t_flags & TF_NOPUSH;			break;		default:			error = ENOPROTOOPT;			break;		}		break;	}	splx(s);	return (error);}/* * tcp_sendspace and tcp_recvspace are the default send and receive window * sizes, respectively.  These are obsolescent (this information should * be set by the route). */u_long	tcp_sendspace = 1024*16;u_long	tcp_recvspace = 1024*16;/* * Attach TCP protocol to socket, allocating * internet protocol control block, tcp control block, * bufer space, and entering LISTEN state if to accept connections. */inttcp_attach(so)	struct socket *so;{	register struct tcpcb *tp;	struct inpcb *inp;	int error;	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {		error = soreserve(so, tcp_sendspace, tcp_recvspace);		if (error)			return (error);	}	error = in_pcballoc(so, &tcbinfo);	if (error)		return (error);	inp = sotoinpcb(so);	tp = tcp_newtcpcb(inp);	if (tp == 0) {		int nofd = so->so_state & SS_NOFDREF;	/* XXX */		so->so_state &= ~SS_NOFDREF;	/* don't free the socket yet */		in_pcbdetach(inp);		so->so_state |= nofd;		return (ENOBUFS);	}	tp->t_state = TCPS_CLOSED;	return (0);}/* * Initiate (or continue) disconnect. * If embryonic state, just send reset (once). * If in ``let data drain'' option and linger null, just drop. * Otherwise (hard), mark socket disconnecting and drop * current input data; switch states based on user close, and * send segment to peer (with FIN). */struct tcpcb *tcp_disconnect(tp)	register struct tcpcb *tp;{	struct socket *so = tp->t_inpcb->inp_socket;	if (tp->t_state < TCPS_ESTABLISHED)		tp = tcp_close(tp);	else if ((so->so_options & SO_LINGER) && so->so_linger == 0)		tp = tcp_drop(tp, 0);	else {		soisdisconnecting(so);		sbflush(&so->so_rcv);		tp = tcp_usrclosed(tp);		if (tp)			(void) tcp_output(tp);	}	return (tp);}/* * User issued close, and wish to trail through shutdown states: * if never received SYN, just forget it.  If got a SYN from peer, * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. * If already got a FIN from peer, then almost done; go to LAST_ACK * state.  In all other cases, have already sent FIN to peer (e.g. * after PRU_SHUTDOWN), and just have to play tedious game waiting * for peer to send FIN or not respond to keep-alives, etc. * We can let the user exit from the close as soon as the FIN is acked. */struct tcpcb *tcp_usrclosed(tp)	register struct tcpcb *tp;{	switch (tp->t_state) {	case TCPS_CLOSED:	case TCPS_LISTEN:		tp->t_state = TCPS_CLOSED;		tp = tcp_close(tp);		break;	case TCPS_SYN_SENT:	case TCPS_SYN_RECEIVED:		tp->t_flags |= TF_NEEDFIN;		break;	case TCPS_ESTABLISHED:		tp->t_state = TCPS_FIN_WAIT_1;		break;	case TCPS_CLOSE_WAIT:		tp->t_state = TCPS_LAST_ACK;		break;	}	if (tp && tp->t_state >= TCPS_FIN_WAIT_2) {		soisdisconnected(tp->t_inpcb->inp_socket);		/* To prevent the connection hanging in FIN_WAIT_2 forever. */		if (tp->t_state == TCPS_FIN_WAIT_2)			tp->t_timer[TCPT_2MSL] = tcp_maxidle;	}	return (tp);}/* * Sysctl for tcp variables. */inttcp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)	int *name;	u_int namelen;	void *oldp;	size_t *oldlenp;	void *newp;	size_t newlen;{	/* All sysctl names at this level are terminal. */	if (namelen != 1)		return (ENOTDIR);	switch (name[0]) {	case TCPCTL_DO_RFC1323:		return (sysctl_int(oldp, oldlenp, newp, newlen,		    &tcp_do_rfc1323));	case TCPCTL_DO_RFC1644:		return (sysctl_int(oldp, oldlenp, newp, newlen,		    &tcp_do_rfc1644));	case TCPCTL_MSSDFLT:		return (sysctl_int(oldp, oldlenp, newp, newlen,		    &tcp_mssdflt));	case TCPCTL_STATS:		return (sysctl_rdstruct(oldp, oldlenp, newp, &tcpstat,					sizeof tcpstat));	case TCPCTL_RTTDFLT:		return (sysctl_int(oldp, oldlenp, newp, newlen, &tcp_rttdflt));	case TCPCTL_KEEPIDLE:		return (sysctl_int(oldp, oldlenp, newp, newlen,				   &tcp_keepidle));	case TCPCTL_KEEPINTVL:		return (sysctl_int(oldp, oldlenp, newp, newlen,				   &tcp_keepintvl));	case TCPCTL_SENDSPACE:		return (sysctl_int(oldp, oldlenp, newp, newlen,				   (int *)&tcp_sendspace)); /* XXX */	case TCPCTL_RECVSPACE:		return (sysctl_int(oldp, oldlenp, newp, newlen,				   (int *)&tcp_recvspace)); /* XXX */	case TCPCTL_KEEPINIT:		return (sysctl_int(oldp, oldlenp, newp, newlen,				   &tcp_keepinit));	default:		return (ENOPROTOOPT);	}	/* NOTREACHED */}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?