#include "../h/param.h" #include "../bbnnet/mbuf.h" #include "../bbnnet/net.h" #include "../bbnnet/ifcb.h" #include "../bbnnet/tcp.h" #include "../bbnnet/ip.h" #include "../bbnnet/icmp.h" #include "../bbnnet/ucb.h" int nosum = 0; /* * IP level input routine * * Called from 1822 level upon receipt of an internet datagram or fragment. * This routine does fragment reassembly, if necessary, and passes completed * datagrams to higher level protocol processing routines on the basis of the * ip header protocol field. It is passed a pointer to an mbuf chain * containing the datagram/fragment. The mbuf offset/length are set to point * at the ip header. */ ip_input(mp, ifp) struct mbuf *mp; struct ifcb *ifp; { register struct ip *ip, *q; register struct ipq *fp; register struct mbuf *m; register i; struct mbuf *n; int hlen; struct ip *p, *savq; struct ipq *ip_findf(); ip = (struct ip *)((int)mp + mp->m_off); /* ->ip hdr */ /* make sure header does not overflow mbuf */ if ((hlen = ip->ip_hl << 2) > mp->m_len) { printf("ip header overflow\n"); netlog(mp); return; } #ifndef mbb i = (unsigned short)ip->ip_sum; #else i = (unsigned short)short_from_net(ip->ip_sum); #endif mbb ip->ip_sum = 0; if (i != (unsigned short)cksum(mp, hlen)) { /* verify checksum */ netstat.ip_badsum++; if (!nosum) { netlog(mp); return; } } ip_from_net(ip); /* byte-swap header */ netcb.n_ip_lock++; /* lock frag reass.q */ fp = ip_findf(ip); /* look for chain on reass.q with this hdr */ /* adjust message length to remove any padding */ for (i=0, m=mp; m != NULL; m = m->m_next) { i += m->m_len; n = m; } i -= ip->ip_len; if (i != 0) if (i > (int)n->m_len) m_adj(mp, -i); else n->m_len -= i; ip->ip_len -= hlen; /* length of data */ ip->ip_mff = ((ip->ip_off & ip_mf) ? TRUE : FALSE); ip->ip_off <<= 3; if (!ip->ip_mff && ip->ip_off == 0) { /* not fragmented */ if (fp != NULL) { /* free existing reass.q chain */ q = fp->iqx.ip_next; /* free mbufs assoc. w/chain */ while (q != (struct ip *)fp) { p = q->ip_next; m_freem(dtom(q)); q = p; } ip_freef(fp); /* free header */ } netcb.n_ip_lock = 0; if (hlen > sizeof(struct ip)) ip_opt(ip, hlen); /* option processing */ i = ip->ip_p; /* pass to next level */ if (i == TCPROTO) tcp_input(mp, ifp); else if (i == GGPROTO) ggp(mp, ifp); else if (i == ICMPROTO) icmp(mp, ifp); else raw_input(mp, ifp, &ip->ip_src, &ip->ip_dst, i, UIP, FALSE); } else { /* -> msg buf beyond ip hdr if not first fragment */ if (ip->ip_off != 0) { mp->m_off += hlen; mp->m_len -= hlen; } if (fp == NULL) { /* first fragment of datagram in */ /* set up reass.q header: enq it, set up as head of frag chain, set a timer value, and move in ip header */ if ((m = m_get(1)) == NULL) { /* allocate an mbuf */ m_freem(mp); return; } fp = (struct ipq *)((int)m + MHEAD); fp->iqx.ip_next = fp->iqx.ip_prev = (struct ip *)fp; bcopy(ip, &fp->iqh, MIN(MLEN-(sizeof(struct ipq)-sizeof(struct ip)), hlen)); fp->iqx.ip_ttl = MAXTTL; fp->iq_next = NULL; fp->iq_prev = netcb.n_ip_tail; if (netcb.n_ip_head != NULL) netcb.n_ip_tail->iq_next = fp; else netcb.n_ip_head = fp; netcb.n_ip_tail = fp; } /* * Merge fragment into reass.q * * Algorithm: Match start and end bytes of new * fragment with fragments on the queue. If no * overlaps are found, add new frag. to the queue. * Otherwise, adjust start and end of new frag. so no * overlap and add remainder to queue. If any * fragments are completely covered by the new one, or * if the new one is completely duplicated, free the * fragments. */ q = fp->iqx.ip_next; /* -> top of reass. chain */ ip->ip_end = ip->ip_off + ip->ip_len - 1; /* skip frags which new doesn't overlap at end */ while ((q != (struct ip *)fp) && (ip->ip_off > q->ip_end)) q = q->ip_next; if (q == (struct ip *)fp) /* frag at end of chain */ ip_enq(ip, fp->iqx.ip_prev); else { /* frag doesn't overlap any */ if (ip->ip_end < q->ip_off) ip_enq(ip, q->ip_prev); /* new overlaps beginning of next frag only */ else if (ip->ip_end < q->ip_end) { if ((i = ip->ip_end-q->ip_off+1) < ip->ip_len) { ip->ip_len -= i; ip->ip_end -= i; m_adj(mp, -i); ip_enq(ip, q->ip_prev); } else m_freem(mp); /* new overlaps end of previous frag */ } else { savq = q; if (ip->ip_off <= q->ip_off) { /* complete cover */ savq = q->ip_prev; ip_deq(q); m_freem(dtom(q)); } else { /* overlap */ if ((i = q->ip_end-ip->ip_off+1) < ip->ip_len) { ip->ip_off += i; ip->ip_len -= i; m_adj(mp, i); } else ip->ip_len = 0; } /* new overlaps at beginning of successor frags */ q = savq->ip_next; while ((q != (struct ip *)fp) && (ip->ip_len != 0) && (q->ip_off < ip->ip_end)) /* complete cover */ if (q->ip_end <= ip->ip_end) { p = q->ip_next; ip_deq(q); m_freem(dtom(q)); q = p; } else { /* overlap */ if ((i = ip->ip_end-q->ip_off+1) < ip->ip_len) { ip->ip_len -= i; ip->ip_end -= i; m_adj(mp, -i); } else ip->ip_len = 0; break; } /* enqueue whatever is left of new before successors */ if (ip->ip_len != 0) ip_enq(ip, savq); else m_freem(mp); } } /* check for completed fragment reassembly */ if ((i = ip_done(fp)) > 0) { p = fp->iqx.ip_next; /* -> top mbuf */ m = dtom(p); p->ip_len = i; /* total data length */ /* option processing */ if ((hlen = p->ip_hl<<2) > sizeof(struct ip)) ip_opt(p, hlen); ip_mergef(fp); /* clean frag chain */ /* copy src/dst internet address to header mbuf */ bcopy(&fp->iqh.ip_src, &p->ip_src, 2*sizeof(struct socket)); ip_freef(fp); /* dequeue header */ netcb.n_ip_lock = 0; /* call next level with completed datagram */ i = p->ip_p; if (i == TCPROTO) tcp_input(m, ifp); else if (i == GGPROTO) ggp(m, ifp); else if (i == ICMPROTO) icmp(m, ifp); else raw_input(m, ifp, &p->ip_src, &p->ip_dst, i, UIP, FALSE); } else netcb.n_ip_lock = 0; } } /* * Check to see if fragment reassembly is complete */ ip_done(p) register struct ip *p; { register struct ip *q; register next; q = p->ip_next; if (q->ip_off != 0) return(0); do { next = q->ip_end + 1; q = q->ip_next; } while ((q != p) && (q->ip_off == next)); if ((q == p) && !(q->ip_prev->ip_mff)) /* all fragments in */ return(next); /* total data length */ else return(0); } /* * Merge mbufs of fragments of completed datagram */ ip_mergef(p) register struct ip *p; { register struct mbuf *m, *n; register struct ip *q; int dummy; q = p->ip_next; /* -> bottom of reass chain */ n = (struct mbuf *)&dummy; /* dummy for init assignment */ while (q != p) { /* through chain */ n->m_next = m = dtom(q); while (m != NULL) if (m->m_len != 0) { n = m; m = m->m_next; } else /* free null mbufs */ n->m_next = m = m_free(m); q = q->ip_next; } } /* * Dequeue and free reass.q header */ ip_freef(fp) register struct ipq *fp; { if (fp->iq_prev != NULL) (fp->iq_prev)->iq_next = fp->iq_next; else netcb.n_ip_head = fp->iq_next; if (fp->iq_next != NULL) (fp->iq_next)->iq_prev = fp->iq_prev; else netcb.n_ip_tail = fp->iq_prev; m_free(dtom(fp)); } /* * Does fragment reassembly chain with this header exist? */ struct ipq *ip_findf(p) register struct ip *p; { register struct ipq *fp; for (fp = netcb.n_ip_head; (fp != NULL && ( p->ip_src.s_addr != fp->iqh.ip_src.s_addr || p->ip_dst.s_addr != fp->iqh.ip_dst.s_addr || p->ip_id != fp->iqh.ip_id || p->ip_p != fp->iqh.ip_p)); fp = fp->iq_next); return(fp); } /* * Process ip options */ ip_opt(ip, hlen) struct ip *ip; int hlen; { register char *p, *q; register i, len; register struct mbuf *m; p = q = (char *)((int)ip + sizeof(struct ip)); /* -> at options */ if ((i = hlen - sizeof(struct ip)) > 0) { /* any options */ /* *** IP OPTION PROCESSING *** while (i > 0) switch (*q++) { case 0: case 1: i--; break; default: i -= *q; q += *q; } */ q += i; m = dtom(q); len = (int)m + m->m_off + m->m_len - (int)q; bcopy((caddr_t)q, (caddr_t)p, len); /* remove options */ m->m_len -= i; } } /* * Enqueue a fragment on the reassembly chain. */ ip_enq(p, prev) register struct ip *p; register struct ip *prev; { p->ip_prev = prev; p->ip_next = prev->ip_next; prev->ip_next->ip_prev = p; prev->ip_next = p; } /* * Dequeue a fragment from reassembly chain. */ ip_deq(p) register struct ip *p; { p->ip_prev->ip_next = p->ip_next; p->ip_next->ip_prev = p->ip_prev; } /* * Take the non-byte data in the ip header as it comes in from the net * and convert it to the form used internally (i.e., either byte swap * or squeeze). */ ip_from_net(p) register struct ip *p; { p->ip_len = short_from_net(p->ip_len); p->ip_id = short_from_net(p->ip_id); p->ip_off = short_from_net(p->ip_off); } /* * IP fragment reassembly timeout routine. */ ip_timeo() { register struct ip *p, *q; register struct ipq *fp; timeout(ip_timeo, 0, HZ); /* reschedule every second */ if (netcb.n_ip_lock) /* reass.q must not be in use */ return; /* search through reass.q */ for (fp = netcb.n_ip_head; fp != NULL; fp = fp->iq_next) if (--(fp->iqx.ip_ttl) == 0) { /* time to die */ q = fp->iqx.ip_next; /* free mbufs assoc. w/chain */ while (q != (struct ip *)fp) { p = q->ip_next; m_freem(dtom(q)); q = p; } ip_freef(fp); /* free header */ } }