📄 tcp_subr.c
字号:
if (isipv6) {
nth->th_sum = 0;
nth->th_sum = in6_cksum(m, IPPROTO_TCP,
sizeof(struct ip6_hdr),
tlen - sizeof(struct ip6_hdr));
ip6->ip6_hlim = in6_selecthlim(tp ? tp->t_inpcb : NULL,
ro6 && ro6->ro_rt ?
ro6->ro_rt->rt_ifp :
NULL);
} 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 = TCPTV_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);
}
void
tcp_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 void
tcp_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
}
void
tcp_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;
if (cmd == PRC_QUENCH)
notify = tcp_quench;
else if (icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB ||
cmd == PRC_UNREACH_PORT) && ip)
notify = tcp_drop_syn_sent;
else if (cmd == PRC_MSGSIZE)
notify = tcp_mtudisc;
else if (PRC_IS_REDIRECT(cmd)) {
ip = 0;
notify = in_rtchange;
} else if (cmd == PRC_HOSTDEAD)
ip = 0;
else if ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)
return;
if (ip) {
s = splnet();
th = (struct tcphdr *)((caddr_t)ip
+ (IP_VHL_HL(ip->ip_vhl) << 2));
inp = in_pcblookup_hash(&tcbinfo, faddr, th->th_dport,
ip->ip_src, th->th_sport, 0, NULL);
if (inp != NULL && inp->inp_socket != NULL) {
icmp_seq = htonl(th->th_seq);
tp = intotcpcb(inp);
if (SEQ_GEQ(icmp_seq, tp->snd_una) &&
SEQ_LT(icmp_seq, tp->snd_max))
(*notify)(inp, inetctlerrmap[cmd]);
}
splx(s);
} else
in_pcbnotifyall(&tcb, faddr, inetctlerrmap[cmd], notify);
}
#ifdef INET6
void
tcp6_ctlinput(cmd, sa, d)
int cmd;
struct sockaddr *sa;
void *d;
{
struct tcphdr th;
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
struct ip6_hdr *ip6;
struct mbuf *m;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -