/* * Free a fragment reassembly header and all * associated datagrams. */ void ip_freef(struct ipq *fp) { struct ipasfrag *q, *p; for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; q = p) { p = (struct ipasfrag *) q->ipf_next; ip_deq(q); m_freem(dtom(q)); } remque_32(fp); (void) m_free(dtom(fp)); }
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==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) { STAT(tcpstat.tcps_rcvduppack++); STAT(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); } STAT(tcpstat.tcps_rcvoopack++); STAT(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); remque_32((void *)(q->ti_prev)); m_freem(m); } /* * Stick new segment in its place. */ insque_32(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; remque_32(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); }
/* * Take incoming datagram fragment and try to * reassemble it into whole datagram. If a chain for * reassembly of this datagram already exists, then it * is given as fp; otherwise have to make a chain. */ struct ip *ip_reass(struct ipasfrag *ip, struct ipq *fp) { struct mbuf *m = dtom(ip); struct ipasfrag *q; int hlen = ip->ip_hl << 2; int i, next; DEBUG_CALL("ip_reass"); DEBUG_ARG("ip = %lx", (long)ip); DEBUG_ARG("fp = %lx", (long)fp); DEBUG_ARG("m = %lx", (long)m); /* * Presence of header sizes in mbufs * would confuse code below. * Fragment m_data is concatenated. */ m->m_data += hlen; m->m_len -= hlen; /* * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { struct mbuf *t; if ((t = m_get()) == NULL) goto dropfrag; fp = mtod(t, struct ipq *); insque_32(fp, &ipq); fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ip->ip_p; fp->ipq_id = ip->ip_id; fp->ipq_next = fp->ipq_prev = (ipasfragp_32)fp; fp->ipq_src = ((struct ip *)ip)->ip_src; fp->ipq_dst = ((struct ip *)ip)->ip_dst; q = (struct ipasfrag *)fp; goto insert; } /* * Find a segment which begins after this one does. */ for (q = (struct ipasfrag *)fp->ipq_next; q != (struct ipasfrag *)fp; q = (struct ipasfrag *)q->ipf_next) if (q->ip_off > ip->ip_off) 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 (q->ipf_prev != (ipasfragp_32)fp) { i = ((struct ipasfrag *)(q->ipf_prev))->ip_off + ((struct ipasfrag *)(q->ipf_prev))->ip_len - ip->ip_off; if (i > 0) { if (i >= ip->ip_len) goto dropfrag; m_adj(dtom(ip), i); ip->ip_off += i; ip->ip_len -= i; } } /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (q != (struct ipasfrag *)fp && ip->ip_off + ip->ip_len > q->ip_off) { i = (ip->ip_off + ip->ip_len) - q->ip_off; if (i < q->ip_len) { q->ip_len -= i; q->ip_off += i; m_adj(dtom(q), i); break; } q = (struct ipasfrag *) q->ipf_next; m_freem(dtom((struct ipasfrag *) q->ipf_prev)); ip_deq((struct ipasfrag *) q->ipf_prev); } insert: /* * Stick new segment in its place; * check for complete reassembly. */ ip_enq(ip, (struct ipasfrag *) q->ipf_prev); next = 0; for (q = (struct ipasfrag *) fp->ipq_next; q != (struct ipasfrag *)fp; q = (struct ipasfrag *) q->ipf_next) { if (q->ip_off != next) return (0); next += q->ip_len; } if (((struct ipasfrag *)(q->ipf_prev))->ipf_mff & 1) return (0); /* * Reassembly is complete; concatenate fragments. */ q = (struct ipasfrag *) fp->ipq_next; m = dtom(q); q = (struct ipasfrag *) q->ipf_next; while (q != (struct ipasfrag *)fp) { struct mbuf *t; t = dtom(q); q = (struct ipasfrag *) q->ipf_next; m_cat(m, t); } /* * Create header for new ip packet by * modifying header of first packet; * dequeue and discard fragment reassembly header. * Make header visible. */ ip = (struct ipasfrag *) fp->ipq_next; /* * If the fragments concatenated to an mbuf that's * bigger than the total size of the fragment, then and * m_ext buffer was alloced. But fp->ipq_next points to * the old buffer (in the mbuf), so we must point ip * into the new buffer. */ if (m->m_flags & M_EXT) { int delta; delta = (char *)ip - m->m_dat; ip = (struct ipasfrag *)(m->m_ext + delta); } /* DEBUG_ARG("ip = %lx", (long)ip); * ip=(struct ipasfrag *)m->m_data; */ ip->ip_len = next; ip->ipf_mff &= ~1; ((struct ip *)ip)->ip_src = fp->ipq_src; ((struct ip *)ip)->ip_dst = fp->ipq_dst; remque_32(fp); (void) m_free(dtom(fp)); m = dtom(ip); m->m_len += (ip->ip_hl << 2); m->m_data -= (ip->ip_hl << 2); return ((struct ip *)ip); dropfrag: ipstat.ips_fragdropped++; m_freem(m); return (0); }