Changeset View
Changeset View
Standalone View
Standalone View
FreeBSD/sys/netinet/tcp_reass.c
Show First 20 Lines • Show All 158 Lines • ▼ Show 20 Lines | tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m) | ||||
/* | /* | ||||
* Call with th==NULL after become established to | * Call with th==NULL after become established to | ||||
* force pre-ESTABLISHED data up to user socket. | * force pre-ESTABLISHED data up to user socket. | ||||
*/ | */ | ||||
if (th == NULL) | if (th == NULL) | ||||
goto present; | goto present; | ||||
KASSERT(SEQ_GEQ(th->th_seq, tp->rcv_nxt), | |||||
("Attempt to add old entry to reassembly queue (th=%p, tp=%p)", | |||||
th, tp)); | |||||
/* | /* | ||||
* Limit the number of segments that can be queued to reduce the | * Limit the number of segments that can be queued to reduce the | ||||
* potential for mbuf exhaustion. For best performance, we want to be | * 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 | * able to queue a full window's worth of segments. The size of the | ||||
* socket receive buffer determines our advertised window and grows | * socket receive buffer determines our advertised window and grows | ||||
* automatically when socket buffer autotuning is enabled. Use it as the | * automatically when socket buffer autotuning is enabled. Use it as the | ||||
* basis for our queue limit. | * basis for our queue limit. | ||||
* Always let the missing segment through which caused this queue. | * Always let the missing segment through which caused this queue. | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m) | ||||
* If there is a preceding segment, it may provide some of | * If there is a preceding segment, it may provide some of | ||||
* our data already. If so, drop the data from the incoming | * our data already. If so, drop the data from the incoming | ||||
* segment. If it provides all of our data, drop us. | * segment. If it provides all of our data, drop us. | ||||
*/ | */ | ||||
if (p != NULL) { | if (p != NULL) { | ||||
int i; | int i; | ||||
/* conversion to int (in i) handles seq wraparound */ | /* conversion to int (in i) handles seq wraparound */ | ||||
i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; | i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; | ||||
if (i > 0) { | if (i >= 0) { | ||||
if (i >= *tlenp) { | if (i >= *tlenp) { | ||||
TCPSTAT_INC(tcps_rcvduppack); | TCPSTAT_INC(tcps_rcvduppack); | ||||
TCPSTAT_ADD(tcps_rcvdupbyte, *tlenp); | TCPSTAT_ADD(tcps_rcvdupbyte, *tlenp); | ||||
p->tqe_th->th_flags |= (th->th_seq & TH_FIN); | |||||
m_freem(m); | m_freem(m); | ||||
if (te != &tqs) | if (te != &tqs) | ||||
uma_zfree(tcp_reass_zone, te); | uma_zfree(tcp_reass_zone, te); | ||||
tp->t_segqlen--; | tp->t_segqlen--; | ||||
/* | /* | ||||
* Try to present any queued data | * Try to present any queued data | ||||
* at the left window edge to the user. | * at the left window edge to the user. | ||||
* This is needed after the 3-WHS | * This is needed after the 3-WHS | ||||
* completes. | * completes. | ||||
*/ | */ | ||||
goto present; /* ??? */ | goto present; /* ??? */ | ||||
} | } | ||||
if (i > 0) { | |||||
m_adj(m, i); | m_adj(m, i); | ||||
*tlenp -= i; | *tlenp -= i; | ||||
th->th_seq += i; | th->th_seq += i; | ||||
} | } | ||||
} | } | ||||
} | |||||
tp->t_rcvoopack++; | tp->t_rcvoopack++; | ||||
TCPSTAT_INC(tcps_rcvoopack); | TCPSTAT_INC(tcps_rcvoopack); | ||||
TCPSTAT_ADD(tcps_rcvoobyte, *tlenp); | TCPSTAT_ADD(tcps_rcvoobyte, *tlenp); | ||||
/* | /* | ||||
* While we overlap succeeding segments trim them or, | * While we overlap succeeding segments trim them or, | ||||
* if they are completely covered, dequeue them. | * if they are completely covered, dequeue them. | ||||
*/ | */ | ||||
Show All 21 Lines | tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m) | ||||
te->tqe_th = th; | te->tqe_th = th; | ||||
te->tqe_len = *tlenp; | te->tqe_len = *tlenp; | ||||
if (p == NULL) { | if (p == NULL) { | ||||
LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); | LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); | ||||
} else { | } else { | ||||
KASSERT(te != &tqs, ("%s: temporary stack based entry not " | KASSERT(te != &tqs, ("%s: temporary stack based entry not " | ||||
"first element in queue", __func__)); | "first element in queue", __func__)); | ||||
if (te == &tqs) | |||||
tp->t_segqlen--; | |||||
else | |||||
LIST_INSERT_AFTER(p, te, tqe_q); | LIST_INSERT_AFTER(p, te, tqe_q); | ||||
} | } | ||||
present: | present: | ||||
/* | /* | ||||
* Present data to user, advancing rcv_nxt through | * Present data to user, advancing rcv_nxt through | ||||
* completed sequence space. | * completed sequence space. | ||||
*/ | */ | ||||
if (!TCPS_HAVEESTABLISHED(tp->t_state)) | if (!TCPS_HAVEESTABLISHED(tp->t_state)) | ||||
return (0); | return (0); | ||||
q = LIST_FIRST(&tp->t_segq); | q = LIST_FIRST(&tp->t_segq); | ||||
KASSERT(q == NULL || SEQ_GEQ(q->tqe_th->th_seq, tp->rcv_nxt), | |||||
("Reassembly queue for %p has stale entry at head", tp)); | |||||
if (!q || q->tqe_th->th_seq != tp->rcv_nxt) | if (!q || q->tqe_th->th_seq != tp->rcv_nxt) | ||||
return (0); | return (0); | ||||
SOCKBUF_LOCK(&so->so_rcv); | SOCKBUF_LOCK(&so->so_rcv); | ||||
do { | do { | ||||
tp->rcv_nxt += q->tqe_len; | tp->rcv_nxt += q->tqe_len; | ||||
flags = q->tqe_th->th_flags & TH_FIN; | flags = q->tqe_th->th_flags & TH_FIN; | ||||
nq = LIST_NEXT(q, tqe_q); | nq = LIST_NEXT(q, tqe_q); | ||||
LIST_REMOVE(q, tqe_q); | LIST_REMOVE(q, tqe_q); | ||||
Show All 12 Lines |