tcp_subr.c

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

C
1,397
字号
	} else#endif /* INET6 */      {        nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,	    htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p)));        m->m_pkthdr.csum_flags = CSUM_TCP;        m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);      }#ifdef TCPDEBUG	if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))		tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0);#endif#ifdef IPSEC	if (ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL) != 0) {		m_freem(m);		return;	}#endif#ifdef INET6	if (isipv6) {		(void)ip6_output(m, NULL, ro6, ipflags, NULL, NULL);		if (ro6 == &sro6 && ro6->ro_rt) {			RTFREE(ro6->ro_rt);			ro6->ro_rt = NULL;		}	} else#endif /* INET6 */      {	(void) ip_output(m, NULL, ro, ipflags, NULL);	if (ro == &sro && ro->ro_rt) {		RTFREE(ro->ro_rt);		ro->ro_rt = NULL;	}      }}/* * Create a new TCP control block, making an * empty reassembly queue and hooking it to the argument * protocol control block.  The `inp' parameter must have * come from the zone allocator set up in tcp_init(). */struct tcpcb *tcp_newtcpcb(inp)	struct inpcb *inp;{	struct inp_tp *it;	register struct tcpcb *tp;#ifdef INET6	int isipv6 = (inp->inp_vflag & INP_IPV6) != 0;#endif /* INET6 */	it = (struct inp_tp *)inp;	tp = &it->tcb;	bzero((char *) tp, sizeof(struct tcpcb));	LIST_INIT(&tp->t_segq);	tp->t_maxseg = tp->t_maxopd =#ifdef INET6		isipv6 ? tcp_v6mssdflt :#endif /* INET6 */		tcp_mssdflt;	/* Set up our timeouts. */	callout_init(tp->tt_rexmt = &it->inp_tp_rexmt);	callout_init(tp->tt_persist = &it->inp_tp_persist);	callout_init(tp->tt_keep = &it->inp_tp_keep);	callout_init(tp->tt_2msl = &it->inp_tp_2msl);	callout_init(tp->tt_delack = &it->inp_tp_delack);	if (tcp_do_rfc1323)		tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);	if (tcp_do_rfc1644)		tp->t_flags |= TF_REQ_CC;	tp->t_inpcb = inp;	/* XXX */	/*	 * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no	 * rtt estimate.  Set rttvar so that srtt + 4 * rttvar gives	 * reasonable initial retransmit time.	 */	tp->t_srtt = TCPTV_SRTTBASE;	tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4;	tp->t_rttmin = tcp_rexmit_min;	tp->t_rxtcur = TCPTV_RTOBASE;	tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;	tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;	tp->t_rcvtime = ticks;        /*	 * IPv4 TTL initialization is necessary for an IPv6 socket as well,	 * because the socket may be bound to an IPv6 wildcard address,	 * which may match an IPv4-mapped IPv6 address.	 */	inp->inp_ip_ttl = ip_defttl;	inp->inp_ppcb = (caddr_t)tp;	return (tp);		/* XXX */}/* * Drop a TCP connection, reporting * the specified error.  If connection is synchronized, * then send a RST to peer. */struct tcpcb *tcp_drop(tp, _errno)	register struct tcpcb *tp;	int _errno;{	struct socket *so = tp->t_inpcb->inp_socket;	if (TCPS_HAVERCVDSYN(tp->t_state)) {		tp->t_state = TCPS_CLOSED;		(void) tcp_output(tp);		tcpstat.tcps_drops++;	} else		tcpstat.tcps_conndrops++;	if (_errno == ETIMEDOUT && tp->t_softerror)		_errno = tp->t_softerror;	so->so_error = _errno;	return (tcp_close(tp));}/* * Close a TCP control block: *	discard all space held by the tcp *	discard internet protocol block *	wake up any sleepers */struct tcpcb *tcp_close(tp)	register struct tcpcb *tp;{	register struct tseg_qent *q;	struct inpcb *inp = tp->t_inpcb;	struct socket *so = inp->inp_socket;#ifdef INET6	int isipv6 = (inp->inp_vflag & INP_IPV6) != 0;#endif /* INET6 */	register struct rtentry *rt;	int dosavessthresh;	/*	 * Make sure that all of our timers are stopped before we	 * delete the PCB.	 */	callout_stop(tp->tt_rexmt);	callout_stop(tp->tt_persist);	callout_stop(tp->tt_keep);	callout_stop(tp->tt_2msl);	callout_stop(tp->tt_delack);	/*	 * If we got enough samples through the srtt filter,	 * save the rtt and rttvar in the routing entry.	 * 'Enough' is arbitrarily defined as the 16 samples.	 * 16 samples is enough for the srtt filter to converge	 * to within 5% of the correct value; fewer samples and	 * we could save a very bogus rtt.	 *	 * Don't update the default route's characteristics and don't	 * update anything that the user "locked".	 */	if (tp->t_rttupdated >= 16) {		register u_long i = 0;#ifdef INET6		if (isipv6) {			struct sockaddr_in6 *sin6;			if ((rt = inp->in6p_route.ro_rt) == NULL)				goto no_valid_rt;			sin6 = (struct sockaddr_in6 *)rt_key(rt);			if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))				goto no_valid_rt;		}		else#endif /* INET6 */				if ((rt = inp->inp_route.ro_rt) == NULL ||		    ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr		    == INADDR_ANY)			goto no_valid_rt;		if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {			i = tp->t_srtt *			    (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));			if (rt->rt_rmx.rmx_rtt && i)				/*				 * filter this update to half the old & half				 * the new values, converting scale.				 * See route.h and tcp_var.h for a				 * description of the scaling constants.				 */				rt->rt_rmx.rmx_rtt =				    (rt->rt_rmx.rmx_rtt + i) / 2;			else				rt->rt_rmx.rmx_rtt = i;			tcpstat.tcps_cachedrtt++;		}		if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {			i = tp->t_rttvar *			    (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));			if (rt->rt_rmx.rmx_rttvar && i)				rt->rt_rmx.rmx_rttvar =				    (rt->rt_rmx.rmx_rttvar + i) / 2;			else				rt->rt_rmx.rmx_rttvar = i;			tcpstat.tcps_cachedrttvar++;		}		/*		 * The old comment here said:		 * update the pipelimit (ssthresh) if it has been updated		 * already or if a pipesize was specified & the threshhold		 * got below half the pipesize.  I.e., wait for bad news		 * before we start updating, then update on both good		 * and bad news.		 *		 * But we want to save the ssthresh even if no pipesize is		 * specified explicitly in the route, because such		 * connections still have an implicit pipesize specified		 * by the global tcp_sendspace.  In the absence of a reliable		 * way to calculate the pipesize, it will have to do.		 */		i = tp->snd_ssthresh;		if (rt->rt_rmx.rmx_sendpipe != 0)			dosavessthresh = (i < rt->rt_rmx.rmx_sendpipe / 2);		else			dosavessthresh = (i < so->so_snd.sb_hiwat / 2);		if (((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&		     i != 0 && rt->rt_rmx.rmx_ssthresh != 0)		    || dosavessthresh) {			/*			 * convert the limit from user data bytes to			 * packets then to packet data bytes.			 */			i = (i + tp->t_maxseg / 2) / tp->t_maxseg;			if (i < 2)				i = 2;			i *= (u_long)(tp->t_maxseg +#ifdef INET6				      (isipv6 ? sizeof (struct ip6_hdr) +					       sizeof (struct tcphdr) :#endif				       sizeof (struct tcpiphdr)#ifdef INET6				       )#endif				      );			if (rt->rt_rmx.rmx_ssthresh)				rt->rt_rmx.rmx_ssthresh =				    (rt->rt_rmx.rmx_ssthresh + i) / 2;			else				rt->rt_rmx.rmx_ssthresh = i;			tcpstat.tcps_cachedssthresh++;		}	}	rt = inp->inp_route.ro_rt;	if (rt) {		/* 		 * mark route for deletion if no information is		 * cached.		 */		if ((tp->t_flags & TF_LQ_OVERFLOW) &&		    ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0)){			if (rt->rt_rmx.rmx_rtt == 0)				rt->rt_flags |= RTF_DELCLONE;		}	}    no_valid_rt:	/* free the reassembly queue, if any */	while((q = LIST_FIRST(&tp->t_segq)) != NULL) {		LIST_REMOVE(q, tqe_q);		m_freem(q->tqe_m);		FREE(q, M_TSEGQ);	}	inp->inp_ppcb = NULL;	soisdisconnected(so);#ifdef INET6	if (INP_CHECK_SOCKAF(so, AF_INET6))		in6_pcbdetach(inp);	else#endif /* INET6 */	in_pcbdetach(inp);	tcpstat.tcps_closed++;	return ((struct tcpcb *)0);}voidtcp_drain(){	if (do_tcpdrain)	{		struct inpcb *inpb;		struct tcpcb *tcpb;		struct tseg_qent *te;	/*	 * Walk the tcpbs, if existing, and flush the reassembly queue,	 * if there is one...	 * XXX: The "Net/3" implementation doesn't imply that the TCP	 *      reassembly queue should be flushed, but in a situation	 * 	where we're really low on mbufs, this is potentially	 *  	usefull.		 */		for (inpb = LIST_FIRST(tcbinfo.listhead); inpb;	    		inpb = LIST_NEXT(inpb, inp_list)) {				if ((tcpb = intotcpcb(inpb))) {					while ((te = LIST_FIRST(&tcpb->t_segq))					       != NULL) {					LIST_REMOVE(te, tqe_q);					m_freem(te->tqe_m);					FREE(te, M_TSEGQ);				}			}		}	}}/* * Notify a tcp user of an asynchronous error; * store error as soft error, but wake up user * (for now, won't do anything until can select for soft error). * * Do not wake up user since there currently is no mechanism for * reporting soft errors (yet - a kqueue filter may be added). */static voidtcp_notify(inp, error)	struct inpcb *inp;	int error;{	struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;	/*	 * Ignore some errors if we are hooked up.	 * If connection hasn't completed, has retransmitted several times,	 * and receives a second error, give up now.  This is better	 * than waiting a long time to establish a connection that	 * can never complete.	 */	if (tp->t_state == TCPS_ESTABLISHED &&	     (error == EHOSTUNREACH || error == ENETUNREACH ||	      error == EHOSTDOWN)) {		return;	} else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 &&	    tp->t_softerror)		tcp_drop(tp, error);	else		tp->t_softerror = error;#if 0	wakeup((caddr_t) &so->so_timeo);	sorwakeup(so);	sowwakeup(so);#endif}#ifdef CYGPKG_NET_FREEBSD_SYSCTLstatic inttcp_pcblist(SYSCTL_HANDLER_ARGS){	int error, i, n, s;	struct inpcb *inp, **inp_list;	inp_gen_t gencnt;	struct xinpgen xig;	/*	 * The process of preparing the TCB list is too time-consuming and	 * resource-intensive to repeat twice on every request.	 */	if (req->oldptr == 0) {		n = tcbinfo.ipi_count;		req->oldidx = 2 * (sizeof xig)			+ (n + n/8) * sizeof(struct xtcpcb);		return 0;	}	if (req->newptr != 0)		return EPERM;	/*	 * OK, now we're committed to doing something.	 */	s = splnet();	gencnt = tcbinfo.ipi_gencnt;	n = tcbinfo.ipi_count;	splx(s);	xig.xig_len = sizeof xig;	xig.xig_count = n;	xig.xig_gen = gencnt;	xig.xig_sogen = so_gencnt;	error = SYSCTL_OUT(req, &xig, sizeof xig);	if (error)		return error;	inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK);	if (inp_list == 0)		return ENOMEM;		s = splnet();	for (inp = LIST_FIRST(tcbinfo.listhead), i = 0; inp && i < n;	     inp = LIST_NEXT(inp, inp_list)) {		if (inp->inp_gencnt <= gencnt)			inp_list[i++] = inp;	}	splx(s);	n = i;	error = 0;	for (i = 0; i < n; i++) {		inp = inp_list[i];		if (inp->inp_gencnt <= gencnt) {			struct xtcpcb xt;			caddr_t inp_ppcb;			xt.xt_len = sizeof xt;			/* XXX should avoid extra copy */			bcopy(inp, &xt.xt_inp, sizeof *inp);			inp_ppcb = inp->inp_ppcb;			if (inp_ppcb != NULL)				bcopy(inp_ppcb, &xt.xt_tp, sizeof xt.xt_tp);			else				bzero((char *) &xt.xt_tp, sizeof xt.xt_tp);			if (inp->inp_socket)				sotoxsocket(inp->inp_socket, &xt.xt_socket);			error = SYSCTL_OUT(req, &xt, sizeof xt);		}	}	if (!error) {		/*		 * Give the user an updated idea of our state.		 * If the generation differs from what we told		 * her before, she knows that something happened		 * while we were processing this request, and it		 * might be necessary to retry.		 */		s = splnet();		xig.xig_gen = tcbinfo.ipi_gencnt;		xig.xig_sogen = so_gencnt;		xig.xig_count = tcbinfo.ipi_count;		splx(s);		error = SYSCTL_OUT(req, &xig, sizeof xig);	}	free(inp_list, M_TEMP);	return error;}#endifSYSCTL_PROC(_net_inet_tcp, TCPCTL_PCBLIST, pcblist, CTLFLAG_RD, 0, 0,	    tcp_pcblist, "S,xtcpcb", "List of active TCP connections");voidtcp_ctlinput(cmd, sa, vip)	int cmd;	struct sockaddr *sa;	void *vip;{	struct ip *ip = vip;	struct tcphdr *th;	struct in_addr faddr;	struct inpcb *inp;	struct tcpcb *tp;	void (*notify) __P((struct inpcb *, int)) = tcp_notify;	tcp_seq icmp_seq;	int s;	faddr = ((struct sockaddr_in *)sa)->sin_addr;	if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)		return;

⌨️ 快捷键说明

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