tcp_usrreq.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,132 行 · 第 1/2 页

C
1,132
字号
			 * initialize maxseg/maxopd using peer's cached			 * MSS.			 */#ifdef INET6			if (isipv6)				error = tcp6_connect(tp, nam, p);			else#endif /* INET6 */			error = tcp_connect(tp, nam, p);			if (error)				goto out;			tp->snd_wnd = TTCP_CLIENT_SND_WND;			tcp_mss(tp, -1);		}		if (flags & PRUS_EOF) {			/*			 * Close the send side of the connection after			 * the data is sent.			 */			socantsendmore(so);			tp = tcp_usrclosed(tp);		}		if (tp != NULL) {			if (flags & PRUS_MORETOCOME)				tp->t_flags |= TF_MORETOCOME;			error = tcp_output(tp);			if (flags & PRUS_MORETOCOME)				tp->t_flags &= ~TF_MORETOCOME;		}	} else {		if (sbspace(&so->so_snd) < -512) {			m_freem(m);			error = ENOBUFS;			goto out;		}		/*		 * According to RFC961 (Assigned Protocols),		 * the urgent pointer points to the last octet		 * of urgent data.  We continue, however,		 * to consider it to indicate the first octet		 * of data past the urgent section.		 * Otherwise, snd_up should be one lower.		 */		sbappend(&so->so_snd, m);		if (nam && tp->t_state < TCPS_SYN_SENT) {			/*			 * Do implied connect if not yet connected,			 * initialize window to default value, and			 * initialize maxseg/maxopd using peer's cached			 * MSS.			 */#ifdef INET6			if (isipv6)				error = tcp6_connect(tp, nam, p);			else#endif /* INET6 */			error = tcp_connect(tp, nam, p);			if (error)				goto out;			tp->snd_wnd = TTCP_CLIENT_SND_WND;			tcp_mss(tp, -1);		}		tp->snd_up = tp->snd_una + so->so_snd.sb_cc;		tp->t_force = 1;		error = tcp_output(tp);		tp->t_force = 0;	}	COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 		   ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND));}/* * Abort the TCP. */static inttcp_usr_abort(struct socket *so){	int s = splnet();	int error = 0;	struct inpcb *inp = sotoinpcb(so);	struct tcpcb *tp;	COMMON_START();	tp = tcp_drop(tp, ECONNABORTED);	COMMON_END(PRU_ABORT);}/* * Receive out-of-band data. */static inttcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags){	int s = splnet();	int error = 0;	struct inpcb *inp = sotoinpcb(so);	struct tcpcb *tp;	COMMON_START();	if ((so->so_oobmark == 0 &&	     (so->so_state & SS_RCVATMARK) == 0) ||	    so->so_options & SO_OOBINLINE ||	    tp->t_oobflags & TCPOOB_HADDATA) {		error = EINVAL;		goto out;	}	if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {		error = EWOULDBLOCK;		goto out;	}	m->m_len = 1;	*mtod(m, caddr_t) = tp->t_iobc;	if ((flags & MSG_PEEK) == 0)		tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);	COMMON_END(PRU_RCVOOB);}/* xxx - should be const */struct pr_usrreqs tcp_usrreqs = {	tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind,	tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach,	tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd,	tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown,	in_setsockaddr, sosend, soreceive, sopoll};#ifdef INET6struct pr_usrreqs tcp6_usrreqs = {	tcp_usr_abort, tcp6_usr_accept, tcp_usr_attach, tcp6_usr_bind,	tcp6_usr_connect, pru_connect2_notsupp, in6_control, tcp_usr_detach,	tcp_usr_disconnect, tcp6_usr_listen, in6_mapped_peeraddr, tcp_usr_rcvd,	tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown,	in6_mapped_sockaddr, sosend, soreceive, sopoll};#endif /* INET6 *//* * 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. */static inttcp_connect(tp, nam, p)	register struct tcpcb *tp;	struct sockaddr *nam;	struct proc *p;{	struct inpcb *inp = tp->t_inpcb, *oinp;	struct socket *so = inp->inp_socket;	struct tcpcb *otp;	struct sockaddr_in *sin = (struct sockaddr_in *)nam;	struct sockaddr_in *ifaddr;	struct rmxp_tao *taop;	struct rmxp_tao tao_noncached;	int error;	if (inp->inp_lport == 0) {		error = in_pcbbind(inp, (struct sockaddr *)0, p);		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_hash(inp->inp_pcbinfo,	    sin->sin_addr, sin->sin_port,	    inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr						: ifaddr->sin_addr,	    inp->inp_lport,  0, NULL);	if (oinp) {		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&		otp->t_state == TCPS_TIME_WAIT &&		    (ticks - otp->t_starttime) < tcp_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);	/* 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;	callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);	tp->iss = tcp_new_isn(tp);	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;}#ifdef INET6static inttcp6_connect(tp, nam, p)	register struct tcpcb *tp;	struct sockaddr *nam;	struct proc *p;{	struct inpcb *inp = tp->t_inpcb, *oinp;	struct socket *so = inp->inp_socket;	struct tcpcb *otp;	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;	struct in6_addr *addr6;	struct rmxp_tao *taop;	struct rmxp_tao tao_noncached;	int error;	if (inp->inp_lport == 0) {		error = in6_pcbbind(inp, (struct sockaddr *)0, p);		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 = in6_pcbladdr(inp, nam, &addr6);	if (error)		return error;	oinp = in6_pcblookup_hash(inp->inp_pcbinfo,				  &sin6->sin6_addr, sin6->sin6_port,				  IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)				  ? addr6				  : &inp->in6p_laddr,				  inp->inp_lport,  0, NULL);	if (oinp) {		if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&		    otp->t_state == TCPS_TIME_WAIT &&		    (ticks - otp->t_starttime) < tcp_msl &&		    (otp->t_flags & TF_RCVD_CC))			otp = tcp_close(otp);		else			return EADDRINUSE;	}	if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))		inp->in6p_laddr = *addr6;	inp->in6p_faddr = sin6->sin6_addr;	inp->inp_fport = sin6->sin6_port;	if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != (u_int32_t)NULL)		inp->in6p_flowinfo = sin6->sin6_flowinfo;	in_pcbrehash(inp);	/* 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;	callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);	tp->iss = tcp_new_isn(tp);	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;}#endif /* INET6 *//* * The new sockopt interface makes it possible for us to block in the * copyin/out step (if we take a page fault).  Taking a page fault at * splnet() is probably a Bad Thing.  (Since sockets and pcbs both now * use TSM, there probably isn't any need for this function to run at * splnet() any more.  This needs more examination.) */inttcp_ctloutput(so, sopt)	struct socket *so;	struct sockopt *sopt;{	int	error, opt, optval, s;	struct	inpcb *inp;	struct	tcpcb *tp;	error = 0;	s = splnet();		/* XXX */	inp = sotoinpcb(so);	if (inp == NULL) {		splx(s);		return (ECONNRESET);	}	if (sopt->sopt_level != IPPROTO_TCP) {#ifdef INET6		if (INP_CHECK_SOCKAF(so, AF_INET6))			error = ip6_ctloutput(so, sopt);		else#endif /* INET6 */		error = ip_ctloutput(so, sopt);		splx(s);		return (error);	}	tp = intotcpcb(inp);	switch (sopt->sopt_dir) {	case SOPT_SET:		switch (sopt->sopt_name) {		case TCP_NODELAY:		case TCP_NOOPT:		case TCP_NOPUSH:			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				break;			switch (sopt->sopt_name) {			case TCP_NODELAY:				opt = TF_NODELAY;				break;			case TCP_NOOPT:				opt = TF_NOOPT;				break;			case TCP_NOPUSH:				opt = TF_NOPUSH;				break;			default:				opt = 0; /* dead code to fool gcc */				break;			}			if (optval)				tp->t_flags |= opt;			else				tp->t_flags &= ~opt;			break;		case TCP_MAXSEG:			error = sooptcopyin(sopt, &optval, sizeof optval,					    sizeof optval);			if (error)				break;			if (optval > 0 && optval <= tp->t_maxseg)				tp->t_maxseg = optval;			else				error = EINVAL;			break;		default:			error = ENOPROTOOPT;			break;		}		break;	case SOPT_GET:		switch (sopt->sopt_name) {		case TCP_NODELAY:			optval = tp->t_flags & TF_NODELAY;			break;		case TCP_MAXSEG:			optval = tp->t_maxseg;			break;		case TCP_NOOPT:			optval = tp->t_flags & TF_NOOPT;			break;		case TCP_NOPUSH:			optval = tp->t_flags & TF_NOPUSH;			break;		default:			error = ENOPROTOOPT;			break;		}		if (error == 0)			error = sooptcopyout(sopt, &optval, sizeof optval);		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;SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, CTLFLAG_RW,     &tcp_sendspace , 0, "Maximum outgoing TCP datagram size");u_long	tcp_recvspace = 1024*16;SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_RW,     &tcp_recvspace , 0, "Maximum incoming TCP datagram size");/* * Attach TCP protocol to socket, allocating * internet protocol control block, tcp control block, * bufer space, and entering LISTEN state if to accept connections. */static inttcp_attach(so, p)	struct socket *so;	struct proc *p;{	register struct tcpcb *tp;	struct inpcb *inp;	int error;#ifdef INET6	int isipv6 = INP_CHECK_SOCKAF(so, AF_INET6) != (int)NULL;#endif	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, p);	if (error)		return (error);	inp = sotoinpcb(so);#ifdef INET6	if (isipv6) {		inp->inp_vflag |= INP_IPV6;		inp->in6p_hops = -1;	/* use kernel default */	}	else#endif	inp->inp_vflag |= INP_IPV4;	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 */#ifdef INET6		if (isipv6)			in6_pcbdetach(inp);		else#endif		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). */static 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. */static 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)			callout_reset(tp->tt_2msl, tcp_maxidle,				      tcp_timer_2msl, tp);	}	return (tp);}

⌨️ 快捷键说明

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