static int tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, struct mbuf *m) { register struct tcpiphdr *q; struct socket *so = tp->t_socket; int flags; /* * Call with ti==NULL after become established to * force pre-ESTABLISHED data up to user socket. */ if (ti == NULL) goto present; /* * Find a segment which begins after this one does. */ for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp); q = tcpiphdr_next(q)) if (SEQ_GT(q->ti_seq, ti->ti_seq)) break; /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ if (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) { register int i; q = tcpiphdr_prev(q); /* conversion to int (in i) handles seq wraparound */ i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) { m_free(m); /* * Try to present any queued data * at the left window edge to the user. * This is needed after the 3-WHS * completes. */ goto present; /* ??? */ } m_adj(m, i); ti->ti_len -= i; ti->ti_seq += i; } q = tcpiphdr_next(q); } ti->ti_mbuf = m; /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (!tcpfrag_list_end(q, tp)) { register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; if (i <= 0) break; if (i < q->ti_len) { q->ti_seq += i; q->ti_len -= i; m_adj(q->ti_mbuf, i); break; } q = tcpiphdr_next(q); m = tcpiphdr_prev(q)->ti_mbuf; remque(tcpiphdr2qlink(tcpiphdr_prev(q))); m_free(m); } /* * Stick new segment in its place. */ insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); present: /* * Present data to user, advancing rcv_nxt through * completed sequence space. */ if (!TCPS_HAVEESTABLISHED(tp->t_state)) return (0); ti = tcpfrag_list_first(tp); if (tcpfrag_list_end(ti, tp) || ti->ti_seq != tp->rcv_nxt) return (0); if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) return (0); do { tp->rcv_nxt += ti->ti_len; flags = ti->ti_flags & TH_FIN; remque(tcpiphdr2qlink(ti)); m = ti->ti_mbuf; ti = tcpiphdr_next(ti); if (so->so_state & SS_FCANTSENDMORE) m_free(m); else { if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); } else sbappend(so, m); } } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); return (flags); }
void tcp_timer_keep(void *xtp) { struct tcpcb *tp = xtp; struct tcptemp *t_template; struct inpcb *inp; CURVNET_SET(tp->t_vnet); #ifdef TCPDEBUG int ostate; ostate = tp->t_state; #endif inp = tp->t_inpcb; KASSERT(inp != NULL, ("%s: tp %p tp->t_inpcb == NULL", __func__, tp)); INP_WLOCK(inp); if (callout_pending(&tp->t_timers->tt_keep) || !callout_active(&tp->t_timers->tt_keep)) { INP_WUNLOCK(inp); CURVNET_RESTORE(); return; } callout_deactivate(&tp->t_timers->tt_keep); if ((inp->inp_flags & INP_DROPPED) != 0) { INP_WUNLOCK(inp); CURVNET_RESTORE(); return; } KASSERT((tp->t_timers->tt_flags & TT_STOPPED) == 0, ("%s: tp %p tcpcb can't be stopped here", __func__, tp)); /* * Because we don't regularly reset the keepalive callout in * the ESTABLISHED state, it may be that we don't actually need * to send a keepalive yet. If that occurs, schedule another * call for the next time the keepalive timer might expire. */ if (TCPS_HAVEESTABLISHED(tp->t_state)) { u_int idletime; idletime = ticks - tp->t_rcvtime; if (idletime < TP_KEEPIDLE(tp)) { callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp) - idletime, tcp_timer_keep, tp); INP_WUNLOCK(inp); CURVNET_RESTORE(); return; } } /* * Keep-alive timer went off; send something * or drop connection if idle for too long. */ TCPSTAT_INC(tcps_keeptimeo); if (tp->t_state < TCPS_ESTABLISHED) goto dropit; if ((always_keepalive || inp->inp_socket->so_options & SO_KEEPALIVE) && tp->t_state <= TCPS_CLOSING) { if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) goto dropit; /* * Send a packet designed to force a response * if the peer is up and reachable: * either an ACK if the connection is still alive, * or an RST if the peer has closed the connection * due to timeout or reboot. * Using sequence number tp->snd_una-1 * causes the transmitted zero-length segment * to lie outside the receive window; * by the protocol spec, this requires the * correspondent TCP to respond. */ TCPSTAT_INC(tcps_keepprobe); t_template = tcpip_maketemplate(inp); if (t_template) { tcp_respond(tp, t_template->tt_ipgen, &t_template->tt_t, (struct mbuf *)NULL, tp->rcv_nxt, tp->snd_una - 1, 0); free(t_template, M_TEMP); } callout_reset(&tp->t_timers->tt_keep, TP_KEEPINTVL(tp), tcp_timer_keep, tp); } else callout_reset(&tp->t_timers->tt_keep, TP_KEEPIDLE(tp), tcp_timer_keep, tp); #ifdef TCPDEBUG if (inp->inp_socket->so_options & SO_DEBUG) tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, PRU_SLOWTIMO); #endif TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); INP_WUNLOCK(inp); CURVNET_RESTORE(); return; dropit: TCPSTAT_INC(tcps_keepdrops); if (tcp_inpinfo_lock_add(inp)) { tcp_inpinfo_lock_del(inp, tp); goto out; } tp = tcp_drop(tp, ETIMEDOUT); #ifdef TCPDEBUG if (tp != NULL && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_USER, ostate, tp, (void *)0, (struct tcphdr *)0, PRU_SLOWTIMO); #endif TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO); tcp_inpinfo_lock_del(inp, tp); out: CURVNET_RESTORE(); }
int tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m) { struct socket *so = tp->t_inpcb->inp_socket; struct mbuf *mq, *mp; int flags, wakeup; INP_WLOCK_ASSERT(tp->t_inpcb); /* * XXX: tcp_reass() is rather inefficient with its data structures * and should be rewritten (see NetBSD for optimizations). */ /* * Call with th==NULL after become established to * force pre-ESTABLISHED data up to user socket. */ if (th == NULL) goto present; M_ASSERTPKTHDR(m); KASSERT(*tlenp == m->m_pkthdr.len, ("%s: tlenp %u len %u", __func__, *tlenp, m->m_pkthdr.len)); /* * Limit the number of segments that can be queued to reduce the * potential for mbuf exhaustion. For best performance, we want to be * able to queue a full window's worth of segments. The size of the * socket receive buffer determines our advertised window and grows * automatically when socket buffer autotuning is enabled. Use it as the * basis for our queue limit. * Always let the missing segment through which caused this queue. * NB: Access to the socket buffer is left intentionally unlocked as we * can tolerate stale information here. */ if ((th->th_seq != tp->rcv_nxt || !TCPS_HAVEESTABLISHED(tp->t_state)) && tp->t_segqlen + m->m_pkthdr.len >= sbspace(&so->so_rcv)) { char *s; TCPSTAT_INC(tcps_rcvreassfull); *tlenp = 0; if ((s = tcp_log_addrs(&tp->t_inpcb->inp_inc, th, NULL, NULL))) { log(LOG_DEBUG, "%s; %s: queue limit reached, " "segment dropped\n", s, __func__); free(s, M_TCPLOG); } m_freem(m); return (0); } /* * Find a segment which begins after this one does. */ mp = NULL; for (mq = tp->t_segq; mq != NULL; mq = mq->m_nextpkt) { if (SEQ_GT(M_TCPHDR(mq)->th_seq, th->th_seq)) break; mp = mq; } /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ if (mp != NULL) { int i; /* conversion to int (in i) handles seq wraparound */ i = M_TCPHDR(mp)->th_seq + mp->m_pkthdr.len - th->th_seq; if (i > 0) { if (i >= *tlenp) { TCPSTAT_INC(tcps_rcvduppack); TCPSTAT_ADD(tcps_rcvdupbyte, *tlenp); m_freem(m); /* * Try to present any queued data * at the left window edge to the user. * This is needed after the 3-WHS * completes. */ goto present; /* ??? */ } m_adj(m, i); *tlenp -= i; th->th_seq += i; } } tp->t_rcvoopack++; TCPSTAT_INC(tcps_rcvoopack); TCPSTAT_ADD(tcps_rcvoobyte, *tlenp); /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (mq) { struct mbuf *nq; int i; i = (th->th_seq + *tlenp) - M_TCPHDR(mq)->th_seq; if (i <= 0) break; if (i < mq->m_pkthdr.len) { M_TCPHDR(mq)->th_seq += i; m_adj(mq, i); tp->t_segqlen -= i; break; } nq = mq->m_nextpkt; tp->t_segqlen -= mq->m_pkthdr.len; m_freem(mq); if (mp) mp->m_nextpkt = nq; else tp->t_segq = nq; mq = nq; } /* * Insert the new segment queue entry into place. Try to collapse * mbuf chains if segments are adjacent. */ if (mp) { if (M_TCPHDR(mp)->th_seq + mp->m_pkthdr.len == th->th_seq) m_catpkt(mp, m); else { m->m_nextpkt = mp->m_nextpkt; mp->m_nextpkt = m; m->m_pkthdr.pkt_tcphdr = th; } } else { mq = tp->t_segq; tp->t_segq = m; if (mq && th->th_seq + *tlenp == M_TCPHDR(mq)->th_seq) { m->m_nextpkt = mq->m_nextpkt; mq->m_nextpkt = NULL; m_catpkt(m, mq); } else m->m_nextpkt = mq; m->m_pkthdr.pkt_tcphdr = th; } tp->t_segqlen += *tlenp; present: /* * Present data to user, advancing rcv_nxt through * completed sequence space. */ if (!TCPS_HAVEESTABLISHED(tp->t_state)) return (0); flags = 0; wakeup = 0; SOCKBUF_LOCK(&so->so_rcv); while ((mq = tp->t_segq) != NULL && M_TCPHDR(mq)->th_seq == tp->rcv_nxt) { tp->t_segq = mq->m_nextpkt; tp->rcv_nxt += mq->m_pkthdr.len; tp->t_segqlen -= mq->m_pkthdr.len; flags = M_TCPHDR(mq)->th_flags & TH_FIN; if (so->so_rcv.sb_state & SBS_CANTRCVMORE) m_freem(mq); else { mq->m_nextpkt = NULL; sbappendstream_locked(&so->so_rcv, mq, 0); wakeup = 1; } } ND6_HINT(tp); if (wakeup) sorwakeup_locked(so); else SOCKBUF_UNLOCK(&so->so_rcv); return (flags); }
void tcp_timer_keep(void *arg) { struct tcpcb *tp = arg; struct socket *so = NULL; /* Quell compiler warning */ #ifdef TCP_DEBUG short ostate; #endif mutex_enter(softnet_lock); if ((tp->t_flags & TF_DEAD) != 0) { mutex_exit(softnet_lock); return; } if (!callout_expired(&tp->t_timer[TCPT_KEEP])) { mutex_exit(softnet_lock); return; } KERNEL_LOCK(1, NULL); #ifdef TCP_DEBUG ostate = tp->t_state; #endif /* TCP_DEBUG */ /* * Keep-alive timer went off; send something * or drop connection if idle for too long. */ TCP_STATINC(TCP_STAT_KEEPTIMEO); if (TCPS_HAVEESTABLISHED(tp->t_state) == 0) goto dropit; #ifdef INET if (tp->t_inpcb) so = tp->t_inpcb->inp_socket; #endif #ifdef INET6 if (tp->t_in6pcb) so = tp->t_in6pcb->in6p_socket; #endif KASSERT(so != NULL); if (so->so_options & SO_KEEPALIVE && tp->t_state <= TCPS_CLOSE_WAIT) { if ((tp->t_maxidle > 0) && ((tcp_now - tp->t_rcvtime) >= tp->t_keepidle + tp->t_maxidle)) goto dropit; /* * Send a packet designed to force a response * if the peer is up and reachable: * either an ACK if the connection is still alive, * or an RST if the peer has closed the connection * due to timeout or reboot. * Using sequence number tp->snd_una-1 * causes the transmitted zero-length segment * to lie outside the receive window; * by the protocol spec, this requires the * correspondent TCP to respond. */ TCP_STATINC(TCP_STAT_KEEPPROBE); if (tcp_compat_42) { /* * The keepalive packet must have nonzero * length to get a 4.2 host to respond. */ (void)tcp_respond(tp, tp->t_template, NULL, NULL, tp->rcv_nxt - 1, tp->snd_una - 1, 0); } else { (void)tcp_respond(tp, tp->t_template, NULL, NULL, tp->rcv_nxt, tp->snd_una - 1, 0); } TCP_TIMER_ARM(tp, TCPT_KEEP, tp->t_keepintvl); } else TCP_TIMER_ARM(tp, TCPT_KEEP, tp->t_keepidle); #ifdef TCP_DEBUG if (tp && so->so_options & SO_DEBUG) tcp_trace(TA_USER, ostate, tp, NULL, PRU_SLOWTIMO | (TCPT_KEEP << 8)); #endif KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); return; dropit: TCP_STATINC(TCP_STAT_KEEPDROPS); (void) tcp_drop(tp, ETIMEDOUT); KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); }
int tcp_reass(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m) { register struct tcpiphdr *q; struct socket *so = tp->t_socket; int flags; /* * Call with ti==0 after become established to * force pre-ESTABLISHED data up to user socket. */ if (ti == 0) goto present; /* * Find a segment which begins after this one does. */ for (q = (struct tcpiphdr *)tp->seg_next; q != (struct tcpiphdr *)tp; q = (struct tcpiphdr *)q->ti_next) if (SEQ_GT(q->ti_seq, ti->ti_seq)) break; /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { register int i; q = (struct tcpiphdr *)q->ti_prev; /* conversion to int (in i) handles seq wraparound */ i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) { tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += ti->ti_len; m_freem(m); /* * Try to present any queued data * at the left window edge to the user. * This is needed after the 3-WHS * completes. */ goto present; /* ??? */ } m_adj(m, i); ti->ti_len -= i; ti->ti_seq += i; } q = (struct tcpiphdr *)(q->ti_next); } tcpstat.tcps_rcvoopack++; tcpstat.tcps_rcvoobyte += ti->ti_len; REASS_MBUF(ti) = (mbufp_32) m; /* XXX */ /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (q != (struct tcpiphdr *)tp) { register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; if (i <= 0) break; if (i < q->ti_len) { q->ti_seq += i; q->ti_len -= i; m_adj((struct mbuf *) REASS_MBUF(q), i); break; } q = (struct tcpiphdr *)q->ti_next; m = (struct mbuf *) REASS_MBUF((struct tcpiphdr *)q->ti_prev); slirp_remque((void *)(q->ti_prev)); m_freem(m); } /* * Stick new segment in its place. */ slirp_insque(ti, (void *)(q->ti_prev)); present: /* * Present data to user, advancing rcv_nxt through * completed sequence space. */ if (!TCPS_HAVEESTABLISHED(tp->t_state)) return (0); ti = (struct tcpiphdr *) tp->seg_next; if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt) return (0); if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) return (0); do { tp->rcv_nxt += ti->ti_len; flags = ti->ti_flags & TH_FIN; slirp_remque(ti); m = (struct mbuf *) REASS_MBUF(ti); /* XXX */ ti = (struct tcpiphdr *)ti->ti_next; /* if (so->so_state & SS_FCANTRCVMORE) */ if (so->so_state & SS_FCANTSENDMORE) m_freem(m); else { if (so->so_emu) { if (tcp_emu(so,m)) sbappend(so, m); } else sbappend(so, m); } } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); /* sorwakeup(so); */ return (flags); }