📄 tcp_subr.c
字号:
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;
struct ip6ctlparam *ip6cp = NULL;
const struct sockaddr_in6 *sa6_src = NULL;
int off;
struct tcp_portonly {
u_int16_t th_sport;
u_int16_t th_dport;
} *thp;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if (cmd == PRC_QUENCH)
notify = tcp_quench;
else if (cmd == PRC_MSGSIZE)
notify = tcp_mtudisc;
else if (!PRC_IS_REDIRECT(cmd) &&
((unsigned)cmd > PRC_NCMDS || inet6ctlerrmap[cmd] == 0))
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
sa6_src = ip6cp->ip6c_src;
} else {
m = NULL;
ip6 = NULL;
off = 0; /* fool gcc */
sa6_src = &sa6_any;
}
if (ip6) {
/*
* XXX: We assume that when IPV6 is non NULL,
* M and OFF are valid.
*/
/* check if we can safely examine src and dst ports */
if (m->m_pkthdr.len < off + sizeof(*thp))
return;
bzero(&th, sizeof(th));
m_copydata(m, off, sizeof(*thp), (caddr_t)&th);
in6_pcbnotify(&tcb, sa, th.th_dport,
(struct sockaddr *)ip6cp->ip6c_src,
th.th_sport, cmd, NULL, notify);
} else
in6_pcbnotify(&tcb, sa, 0, (struct sockaddr *)sa6_src,
0, cmd, NULL, notify);
}
#endif /* INET6 */
/*
* Following is where TCP initial sequence number generation occurs.
*
* There are two places where we must use initial sequence numbers:
* 1. In SYN-ACK packets.
* 2. In SYN packets.
*
* The ISNs in SYN-ACK packets have no monotonicity requirement,
* and should be as unpredictable as possible to avoid the possibility
* of spoofing and/or connection hijacking. To satisfy this
* requirement, SYN-ACK ISNs are generated via the arc4random()
* function. If exact RFC 1948 compliance is requested via sysctl,
* these ISNs will be generated just like those in SYN packets.
*
* The ISNs in SYN packets must be monotonic; TIME_WAIT recycling
* depends on this property. In addition, these ISNs should be
* unguessable so as to prevent connection hijacking. To satisfy
* the requirements of this situation, the algorithm outlined in
* RFC 1948 is used to generate sequence numbers.
*
* For more information on the theory of operation, please see
* RFC 1948.
*
* Implementation details:
*
* Time is based off the system timer, and is corrected so that it
* increases by one megabyte per second. This allows for proper
* recycling on high speed LANs while still leaving over an hour
* before rollover.
*
* Two sysctls control the generation of ISNs:
*
* net.inet.tcp.isn_reseed_interval controls the number of seconds
* between seeding of isn_secret. This is normally set to zero,
* as reseeding should not be necessary.
*
* net.inet.tcp.strict_rfc1948 controls whether RFC 1948 is followed
* strictly. When strict compliance is requested, reseeding is
* disabled and SYN-ACKs will be generated in the same manner as
* SYNs. Strict mode is disabled by default.
*
*/
#define ISN_BYTES_PER_SECOND 1048576
u_char isn_secret[32];
int isn_last_reseed;
MD5_CTX isn_ctx;
tcp_seq
tcp_new_isn(tp)
struct tcpcb *tp;
{
u_int32_t md5_buffer[4];
tcp_seq new_isn;
/* Use arc4random for SYN-ACKs when not in exact RFC1948 mode. */
if (((tp->t_state == TCPS_LISTEN) || (tp->t_state == TCPS_TIME_WAIT))
&& tcp_strict_rfc1948 == 0)
return arc4random();
/* Seed if this is the first use, reseed if requested. */
if ((isn_last_reseed == 0) ||
((tcp_strict_rfc1948 == 0) && (tcp_isn_reseed_interval > 0) &&
(((u_int)isn_last_reseed + (u_int)tcp_isn_reseed_interval*hz)
< (u_int)ticks))) {
read_random_unlimited(&isn_secret, sizeof(isn_secret));
isn_last_reseed = ticks;
}
/* Compute the md5 hash and return the ISN. */
MD5Init(&isn_ctx);
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_fport, sizeof(u_short));
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_lport, sizeof(u_short));
#ifdef INET6
if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) {
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_faddr,
sizeof(struct in6_addr));
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_laddr,
sizeof(struct in6_addr));
} else
#endif
{
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_faddr,
sizeof(struct in_addr));
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_laddr,
sizeof(struct in_addr));
}
MD5Update(&isn_ctx, (u_char *) &isn_secret, sizeof(isn_secret));
MD5Final((u_char *) &md5_buffer, &isn_ctx);
new_isn = (tcp_seq) md5_buffer[0];
new_isn += ticks * (ISN_BYTES_PER_SECOND / hz);
return new_isn;
}
/*
* 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;
}
/*
* When a specific ICMP unreachable message is received and the
* connection state is SYN-SENT, drop the connection. This behavior
* is controlled by the icmp_may_rst sysctl.
*/
void
tcp_drop_syn_sent(inp, _errno)
struct inpcb *inp;
int _errno;
{
struct tcpcb *tp = intotcpcb(inp);
if (tp && tp->t_state == TCPS_SYN_SENT)
tcp_drop(tp, _errno);
}
/*
* When `need fragmentation' ICMP is received, update our idea of the MSS
* based on the new value in the route. Also nudge TCP to send something,
* since we know the packet we just sent was dropped.
* This duplicates some code in the tcp_mss() function in tcp_input.c.
*/
void
tcp_mtudisc(inp, _errno)
struct inpcb *inp;
int _errno;
{
struct tcpcb *tp = intotcpcb(inp);
struct rtentry *rt;
struct rmxp_tao *taop;
struct socket *so = inp->inp_socket;
int offered;
int mss;
#ifdef INET6
int isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0;
#endif /* INET6 */
if (tp) {
#ifdef INET6
if (isipv6)
rt = tcp_rtlookup6(inp);
else
#endif /* INET6 */
rt = tcp_rtlookup(inp);
if (!rt || !rt->rt_rmx.rmx_mtu) {
tp->t_maxopd = tp->t_maxseg =
#ifdef INET6
isipv6 ? tcp_v6mssdflt :
#endif /* INET6 */
tcp_mssdflt;
return;
}
taop = rmx_taop(rt->rt_rmx);
offered = taop->tao_mssopt;
mss = rt->rt_rmx.rmx_mtu -
#ifdef INET6
(isipv6 ?
sizeof(struct ip6_hdr) + sizeof(struct tcphdr) :
#endif /* INET6 */
sizeof(struct tcpiphdr)
#ifdef INET6
)
#endif /* INET6 */
;
if (offered)
mss = min(mss, offered);
/*
* XXX - The above conditional probably violates the TCP
* spec. The problem is that, since we don't know the
* other end's MSS, we are supposed to use a conservative
* default. But, if we do that, then MTU discovery will
* never actually take place, because the conservative
* default is much less than the MTUs typically seen
* on the Internet today. For the moment, we'll sweep
* this under the carpet.
*
* The conservative default might not actually be a problem
* if the only case this occurs is when sending an initial
* SYN with options and data to a host we've never talked
* to before. Then, they will reply with an MSS value which
* will get recorded and the new parameters should get
* recomputed. For Further Study.
*/
if (tp->t_maxopd <= mss)
return;
tp->t_maxopd = mss;
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
(tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)
mss -= TCPOLEN_TSTAMP_APPA;
if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC &&
(tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)
mss -= TCPOLEN_CC_APPA;
#if (MCLBYTES & (MCLBYTES - 1)) == 0
if (mss > MCLBYTES)
mss &= ~(MCLBYTES-1);
#else
if (mss > MCLBYTES)
mss = mss / MCLBYTES * MCLBYTES;
#endif
if (so->so_snd.sb_hiwat < mss)
mss = so->so_snd.sb_hiwat;
tp->t_maxseg = mss;
tcpstat.tcps_mturesent++;
tp->t_rtttime = 0;
tp->snd_nxt = tp->snd_una;
tcp_output(tp);
}
}
/*
* Look-up the routing entry to the peer of this inpcb. If no route
* is found and it cannot be allocated the return NULL. This routine
* is called by TCP routines that access the rmx structure and by tcp_mss
* to get the interface MTU.
*/
struct rtentry *
tcp_rtlookup(inp)
struct inpcb *inp;
{
struct route *ro;
struct rtentry *rt;
ro = &inp->inp_route;
rt = ro->ro_rt;
if (rt == NULL || !(rt->rt_flags & RTF_UP)) {
/* No route yet, so try to acquire one */
if (inp->inp_faddr.s_addr != INADDR_ANY) {
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
inp->inp_faddr;
rtalloc(ro);
rt = ro->ro_rt;
}
}
return rt;
}
#ifdef INET6
struct rtentry *
tcp_rtlookup6(inp)
struct inpcb *inp;
{
#ifdef NEW_STRUCT_ROUTE
struct route *ro6;
#else
struct route_in6 *ro6;
#endif
struct rtentry *rt;
ro6 = &inp->in6p_route;
rt = ro6->ro_rt;
if (rt == NULL || !(rt->rt_flags & RTF_UP)) {
/* No route yet, so try to acquire one */
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
struct sockaddr_in6 *dst6;
dst6 = (struct sockaddr_in6 *)&ro6->ro_dst;
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = inp->in6p_faddr;
rtalloc((struct route *)ro6);
rt = ro6->ro_rt;
}
}
return rt;
}
#endif /* INET6 */
#ifdef IPSEC
/* compute ESP/AH header size for TCP, including outer IP header. */
size_t
ipsec_hdrsiz_tcp(tp)
struct tcpcb *tp;
{
struct inpcb *inp;
struct mbuf *m;
size_t hdrsiz;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
struct tcphdr *th;
if ((tp == NULL) || ((inp = tp->t_inpcb) == NULL))
return 0;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (!m)
return 0;
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0) {
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)(ip6 + 1);
m->m_pkthdr.len = m->m_len =
sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
tcp_fillheaders(tp, ip6, th);
hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
} else
#endif /* INET6 */
{
ip = mtod(m, struct ip *);
th = (struct tcphdr *)(ip + 1);
m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr);
tcp_fillheaders(tp, ip, th);
hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
}
m_free(m);
return hdrsiz;
}
#endif /*IPSEC*/
/*
* Return a pointer to the cached information about the remote host.
* The cached information is stored in the protocol specific part of
* the route metrics.
*/
struct rmxp_tao *
tcp_gettaocache(inp)
struct inpcb *inp;
{
struct rtentry *rt;
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0)
rt = tcp_rtlookup6(inp);
else
#endif /* INET6 */
rt = tcp_rtlookup(inp);
/* Make sure this is a host route and is up. */
if (rt == NULL ||
(rt->rt_flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST))
return NULL;
return rmx_taop(rt->rt_rmx);
}
/*
* Clear all the TAO cache entries, called from tcp_init.
*
* XXX
* This routine is just an empty one, because we assume that the routing
* routing tables are initialized at the same time when TCP, so there is
* nothing in the cache left over.
*/
static void
tcp_cleartaocache()
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -