📄 tcp_subr.c
字号:
tcp_newtcpcb(inp)
struct inpcb *inp;
{
register struct tcpcb *tp;
tp = malloc(sizeof(*tp), M_PCB, M_NOWAIT);
if (tp == NULL)
return ((struct tcpcb *)0);
bzero((char *) tp, sizeof(struct tcpcb));
LIST_INIT(&tp->segq);
tp->t_maxseg = tp->t_maxopd = tcp_mssdflt;
#ifdef TCP_SACK
tp->sack_disable = tcp_do_sack ? 0 : 1;
#endif
tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
tp->t_inpcb = inp;
/*
* Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
* rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
* reasonable initial retransmit time.
*/
tp->t_srtt = TCPTV_SRTTBASE;
tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << (TCP_RTTVAR_SHIFT + 2 - 1);
tp->t_rttmin = TCPTV_MIN;
TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
TCPTV_MIN, TCPTV_REXMTMAX);
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
#ifdef INET6
/*
* If we want to use tp->pf for a quick-n-easy way to determine
* the outbound dgram type, we cannot make this decision
* until a connection is established! Bzero() sets pf to zero, and
* that's the way we want it, unless, of course, it's an AF_INET
* socket...
*/
if ((inp->inp_flags & INP_IPV6) == 0)
tp->pf = PF_INET; /* If AF_INET socket, we can't do v6 from it. */
#else
tp->pf = PF_INET;
#endif
#ifdef INET6
if (inp->inp_flags & INP_IPV6)
inp->inp_ipv6.ip6_hlim = ip6_defhlim;
else
#endif /* INET6 */
inp->inp_ip.ip_ttl = ip_defttl;
inp->inp_ppcb = (caddr_t)tp;
return (tp);
}
/*
* 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 ipqent *qe;
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
#ifdef TCP_SACK
struct sackhole *p, *q;
#endif
#ifdef RTV_RTT
register struct rtentry *rt;
#ifdef INET6
register int bound_to_specific = 0; /* I.e. non-default */
/*
* This code checks the nature of the route for this connection.
* Normally this is done by two simple checks in the next
* INET/INET6 ifdef block, but because of two possible lower layers,
* that check is done here.
*
* Perhaps should be doing this only for a RTF_HOST route.
*/
rt = inp->inp_route.ro_rt; /* Same for route or route6. */
if (tp->pf == PF_INET6) {
if (rt)
bound_to_specific =
!(IN6_IS_ADDR_UNSPECIFIED(&
((struct sockaddr_in6 *)rt_key(rt))->sin6_addr));
} else {
if (rt)
bound_to_specific =
(((struct sockaddr_in *)rt_key(rt))->
sin_addr.s_addr != INADDR_ANY);
}
#endif /* INET6 */
/*
* If we sent enough data to get some meaningful characteristics,
* save them in the routing entry. 'Enough' is arbitrarily
* defined as the sendpipesize (default 4K) * 16. This would
* give us 16 rtt samples assuming we only get one sample per
* window (the usual case on a long haul net). 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".
*/
#ifdef INET6
/*
* Note that rt and bound_to_specific are set above.
*/
if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
rt && bound_to_specific) {
#else /* INET6 */
if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
(rt = inp->inp_route.ro_rt) &&
satosin(rt_key(rt))->sin_addr.s_addr != INADDR_ANY) {
#endif /* INET6 */
register u_long i = 0;
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
i = tp->t_srtt *
(RTM_RTTUNIT / (PR_SLOWHZ * 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;
}
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
i = tp->t_rttvar *
(RTM_RTTUNIT / (PR_SLOWHZ * 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;
}
/*
* 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.
*/
if (((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
(i = tp->snd_ssthresh) && rt->rt_rmx.rmx_ssthresh) ||
i < (rt->rt_rmx.rmx_sendpipe / 2)) {
/*
* 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;
#ifdef INET6
if (tp->pf == PF_INET6)
i *= (u_long)(tp->t_maxseg + sizeof (struct tcphdr)
+ sizeof(struct ip6_hdr));
else
#endif /* INET6 */
i *= (u_long)(tp->t_maxseg +
sizeof (struct tcpiphdr));
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;
}
}
#endif /* RTV_RTT */
/* free the reassembly queue, if any */
#ifdef INET6
/* Reassembling TCP segments in v6 might be sufficiently different
* to merit two codepaths to free the reasssembly queue.
* If an undecided TCP socket, then the IPv4 codepath will be used
* because it won't matter much anyway.
*/
if (tp->pf == AF_INET6) {
while ((qe = tp->segq.lh_first) != NULL) {
LIST_REMOVE(qe, ipqe_q);
m_freem(qe->ipqe_m);
FREE(qe, M_IPQ);
}
} else
#endif /* INET6 */
while ((qe = tp->segq.lh_first) != NULL) {
LIST_REMOVE(qe, ipqe_q);
m_freem(qe->ipqe_m);
FREE(qe, M_IPQ);
}
#ifdef TCP_SACK
/* Free SACK holes. */
q = p = tp->snd_holes;
while (p != 0) {
q = p->next;
free(p, M_PCB);
p = q;
}
#endif
if (tp->t_template)
(void) m_free(tp->t_template);
free(tp, M_PCB);
inp->inp_ppcb = 0;
soisdisconnected(so);
in_pcbdetach(inp);
tcpstat.tcps_closed++;
return ((struct tcpcb *)0);
}
void
tcp_drain()
{
}
/*
* 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).
*/
void
tcp_notify(inp, error)
struct inpcb *inp;
int error;
{
register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;
register struct socket *so = inp->inp_socket;
/*
* 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 (TCPS_HAVEESTABLISHED(tp->t_state) == 0 &&
tp->t_rxtshift > 3 && tp->t_softerror)
so->so_error = error;
else
tp->t_softerror = error;
wakeup((caddr_t) &so->so_timeo);
sorwakeup(so);
sowwakeup(so);
}
#if defined(INET6) && !defined(TCP6)
void
tcp6_ctlinput(cmd, sa, d)
int cmd;
struct sockaddr *sa;
void *d;
{
(void)tcp_ctlinput(cmd, sa, NULL); /*XXX*/
}
#endif
void *
tcp_ctlinput(cmd, sa, v)
int cmd;
struct sockaddr *sa;
register void *v;
{
register struct ip *ip = v;
register struct tcphdr *th;
extern int inetctlerrmap[];
void (*notify) __P((struct inpcb *, int)) = tcp_notify;
int errno;
if ((unsigned)cmd >= PRC_NCMDS)
return NULL;
errno = inetctlerrmap[cmd];
if (cmd == PRC_QUENCH)
notify = tcp_quench;
else if (PRC_IS_REDIRECT(cmd))
notify = in_rtchange, ip = 0;
else if (cmd == PRC_HOSTDEAD)
ip = 0;
else if (errno == 0)
return NULL;
#ifdef INET6
if (sa->sa_family == AF_INET6) {
if (ip) {
struct ip6_hdr *ipv6 = (struct ip6_hdr *)ip;
th = (struct tcphdr *)(ipv6 + 1);
#if 0 /*XXX*/
in6_pcbnotify(&tcbtable, sa, th->th_dport,
&ipv6->ip6_src, th->th_sport, cmd, notify);
#endif
} else {
#if 0 /*XXX*/
in6_pcbnotify(&tcbtable, sa, 0,
(struct in6_addr *)&in6addr_any, 0, cmd, notify);
#endif
}
} else
#endif /* INET6 */
{
if (ip) {
th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
in_pcbnotify(&tcbtable, sa, th->th_dport, ip->ip_src,
th->th_sport, errno, notify);
} else
in_pcbnotifyall(&tcbtable, sa, errno, notify);
}
return NULL;
}
/*
* When a source quench is received, close congestion window
* to one segment. We will gradually open it again as we proceed.
*/
void
tcp_quench(inp, errno)
struct inpcb *inp;
int errno;
{
struct tcpcb *tp = intotcpcb(inp);
if (tp)
tp->snd_cwnd = tp->t_maxseg;
}
#ifdef TCP_SIGNATURE
int
tcp_signature_tdb_attach()
{
return (0);
}
int
tcp_signature_tdb_init(tdbp, xsp, ii)
struct tdb *tdbp;
struct xformsw *xsp;
struct ipsecinit *ii;
{
char *c;
#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
#define isalpha(c) ( (((c) >= 'A') && ((c) <= 'Z')) || \
(((c) >= 'a') && ((c) <= 'z')) )
if ((ii->ii_authkeylen < 1) || (ii->ii_authkeylen > 80))
return (EINVAL);
c = (char *)ii->ii_authkey;
while (c < (char *)ii->ii_authkey + ii->ii_authkeylen - 1) {
if (isdigit(*c)) {
if (*(c + 1) == ' ')
return (EINVAL);
} else {
if (!isalpha(*c))
return (EINVAL);
}
c++;
}
if (!isdigit(*c) && !isalpha(*c))
return (EINVAL);
tdbp->tdb_amxkey = malloc(ii->ii_authkeylen, M_XDATA, M_DONTWAIT);
if (tdbp->tdb_amxkey == NULL)
return (ENOMEM);
bcopy(ii->ii_authkey, tdbp->tdb_amxkey, ii->ii_authkeylen);
tdbp->tdb_amxkeylen = ii->ii_authkeylen;
return (0);
}
int
tcp_signature_tdb_zeroize(tdbp)
struct tdb *tdbp;
{
if (tdbp->tdb_amxkey) {
bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
free(tdbp->tdb_amxkey, M_XDATA);
tdbp->tdb_amxkey = NULL;
}
return (0);
}
struct mbuf *
tcp_signature_tdb_input(m, tdbp)
struct mbuf *m;
struct tdb *tdbp;
{
return (0);
}
int
tcp_signature_tdb_output(m, tdbp, mp)
struct mbuf *m;
struct tdb *tdbp;
struct mbuf **mp;
{
return (EINVAL);
}
int
tcp_signature_apply(fstate, data, len)
caddr_t fstate;
caddr_t data;
unsigned int len;
{
MD5Update((MD5_CTX *)fstate, (char *)data, len);
return 0;
}
#endif /* TCP_SIGNATURE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -