# #include "../h/param.h" #include "../h/pkts.h" #include "../h/net.h" #define CHAMAX 488 /* Chaos max inet pktsize */ struct freeblock { struct freeblock *f_next; int f_begin; int f_end; }; short inet_fragsz[256]; /* Init internet world */ inn_init() { init_fragsz(); } mk_inet(inp, plen) register struct inet_hdr *inp; short plen; { register short iplen, maxfrag, iphlen; iplen = swab(inp->ip_len); maxfrag = inn_maxfrag(&mymach, &inp->ip_dest); if ( (plen < (iplen + NETHSIZ)) || (plen > (maxfrag + NETHSIZ)) ) return(ERR); inp->ip_src = mymach; if (inp->ip_id == 0) inp->ip_id = swab(gt_unqid()); iphlen = (inp->ip_ihlver & 017) << 1; /* No. of 16 bit words */ inp->ip_chksum = 0; inp->ip_chksum = ~cksum(inp, iphlen, 0); return(0); } ch_inet(inp, plen) register struct inet_hdr *inp; short plen; { register short ipihl; ipihl = ((inp->ip_ihlver & 017) << 2); if ((ipihl > plen) || (ipihl < IPHSIZ)) { neterrt(EIPHLEN); return(FALSE); } if (inp->ip_chksum != 0) { if (~cksum(inp, ipihl >> 1, 0) != 0) { neterrt(EIPCKSUM); return(FALSE); } } if (inp->ip_dest != mymach) { neterrt(EIPDEST); return(FALSE); } inn_swab(inp); if ((inp->ip_len > plen) || (inp->ip_len < IPHSIZ)) { neterrt(EIPLEN); return(FALSE); } return(TRUE); } /* mng_frag_disp * Oversee reassembly * Dispatch complete packet to its owner packet * Queue packet if 1) complete packet w. owner process, * 2) first fragment w/out first fragment sibling, * 3) later fragment w. no first fragment sibling * into which it can be copied */ mng_frag_disp(rp) register struct rcvpkt *rp; { register struct rcvpkt *rp2; register struct inet_hdr *inp; register struct netino *nip; int goodret; inp = &rp->rp_inet; /* Chk whether new packet is fragment */ if ( (inp->ip_foff) || (inp->ip_flgs & MOREFRAGS) ) { /* Chk whether the first fragment sibling exists */ if ((rp2 = inn_fnd_ff(inp)) != NULL) { /* First Frag. sibling found - copy this frag into it */ if ( !inn_cpy_frag(rp2, rp) ) { netp_deque(rp2); return; } /* Chk whether packet complete */ if (!(rp2->rp_flags & N_FFRAG)) { if (!inn_dispatch(rp2)) netp_deque(rp2); else { nip = rp2->rp_inode; if (nip->nt_flags & NINTRUP) psignal(nip->nt_procp, SIGNINT); else nip->nt_flags |= NINTMISS; } } return; } /* No first fragment sibling found */ /* Chk whether later fragment */ if (inp->ip_foff) { rp->rp_flags |= N_FRAG; netp_enque(rp); return; } /* New packet is first fragment w/out first fragment sibling */ if (!inn_assembl_init(inp)) return; rp->rp_flags |= N_FFRAG; rp->rp_len = NETBYTES; /* Look for fragments to copy into this packet */ for (rp2 = inn_fd_sib(inp); (rp2 != NULL) && (rp->rp_flags & N_FFRAG); rp2 = inn_fd_sib(inp) ) { goodret = inn_cpy_frag(rp, rp2); netp_deque(rp2); if (!goodret) return; } if (rp->rp_flags & N_FFRAG) { netp_enque(rp); return; } } /* Packet is complete - dispatch it */ if (!inn_dispatch(rp)) return; netp_enque(rp); nip = rp->rp_inode; if (nip->nt_flags & NINTRUP) psignal(nip->nt_procp, SIGNINT); else nip->nt_flags |= NINTMISS; } /* inn_fnd_ff find first fragment mate for * fragment inp */ inn_fnd_ff(inp) register struct inet_hdr *inp; { register struct rcvpkt *rp; for (rp = rcvpktdir.rd_head; rp != NULL; rp = rp->rp_forw) { if ( (rp->rp_flags & N_FFRAG) && (rp->rp_inet.ip_id == inp->ip_id) && (rp->rp_inet.ip_prot == inp->ip_prot) && (rp->rp_inet.ip_src == inp->ip_src) ) return(rp); } return(NULL); } /* inn_fd_sib find sibling for * fragment inp */ inn_fd_sib(inp) register struct inet_hdr *inp; { register struct rcvpkt *rp; for (rp = rcvpktdir.rd_head; rp != NULL; rp = rp->rp_forw) { if ( (rp->rp_flags & N_FRAG) && (rp->rp_inet.ip_id == inp->ip_id) && (rp->rp_inet.ip_prot == inp->ip_prot) && (rp->rp_inet.ip_src == inp->ip_src) ) return(rp); } return(NULL); } /* inn_assembl_init init first fragment * to manage reassembly into it * Use ip_chksum to mark first free block and * ip_len to indicate highest octet no. received. */ inn_assembl_init(inp) register struct inet_hdr *inp; { register char *chp; register struct freeblock *freep; int iphlen; if (inp->ip_len > MAXINET) { neterrt(EIPLEN); return(FALSE); } iphlen = (inp->ip_ihlver & 017) << 2; chp = inp; chp += inp->ip_len; freep = chp; freep->f_next = NULL; freep->f_begin = inp->ip_len - iphlen; freep->f_end = MAXINET - iphlen - 1; inp->ip_chksum = freep; inp->ip_len = freep->f_begin - 1; return(TRUE); } /* inn_cpy_frag copy fragment rp2 into free * space in rp1 * Chk that new fragment's location in packet is * between 0 and MAXINET - final pkt inet hdr leng * or, if last fragment has arrived and know final * data length, chk that new fragment's location * in packet is between 0 and final data length. * If new fragment is last data fragment, * reset final pkt MOREFRAGS bit in ip_flgs and * chk that no data exists beyond final fragment * If new fragment is not last data fragment, * chk that its length is exact multiple of 8 octets. * Note that rp1 ip_chksum holds ptr to first * freeblock and ip_len holds data offset of last * byte received. */ inn_cpy_frag(rp1, rp2) struct rcvpkt *rp1, *rp2; { int iphlen1, maxdata, iphlen2; int fbegin, fend, fendsv, blen; int fb_end; int foff1, foff2; register struct freeblock *freep; struct freeblock *freeprev; char *fnew; register int *i1, *i2; char *c1, *c2, *cend; iphlen1 = (rp1->rp_inet.ip_ihlver & 017) << 2; if (rp1->rp_inet.ip_flgs & MOREFRAGS) maxdata = MAXINET - iphlen1 - 1; else maxdata = rp1->rp_inet.ip_len; iphlen2 = (rp2->rp_inet.ip_ihlver & 017) << 2; fbegin = rp2->rp_inet.ip_foff << 3; fend = fbegin + (rp2->rp_inet.ip_len - iphlen2) -1; if ((fbegin < 0) || (fbegin > maxdata) || (fend < 0) || (fend > maxdata)) { neterrt(EIPLEN); return(FALSE); } if (!(rp2->rp_inet.ip_flgs & MOREFRAGS)) { rp1->rp_inet.ip_flgs &= (~MOREFRAGS); if (fend < rp1->rp_inet.ip_len) { neterrt(EIPLEN); return(FALSE); } } else if ( ((blen = fend - fbegin + 1) < 8) || ((blen % 8) != 0) ) { neterrt(EIPLEN); return(FALSE); } if (fend > rp1->rp_inet.ip_len) rp1->rp_inet.ip_len = fend; fendsv = fend; for (freeprev = &rp1->rp_inet.ip_chksum, freep = rp1->rp_inet.ip_chksum; freep != NULL; ) { if (fend < freep->f_begin) break; if (fbegin > freep->f_end) { freeprev = freep; freep = freep->f_next; continue; } fb_end = freep->f_end; if (freep->f_begin < fbegin) { /* shorten freeblock */ freep->f_end = fbegin - 1; freeprev = freep; foff1 = fbegin; foff2 = 0; } else { /* drop freeblock */ freeprev->f_next = freep->f_next; foff1 = freep->f_begin; foff2 = foff1 - fbegin; } if (fb_end > fend) { /* add freeblock */ fnew = &rp1->rp_inet; fnew += (iphlen1 + fend + 1); if (fnew & 01) fnew++; fnew->f_begin = fend + 1; fnew->f_end = fb_end; fnew->f_next = freeprev->f_next; freeprev->f_next = fnew; freeprev = fnew; freep = fnew; } else if (fend > fb_end) fend = fb_end; freep = freep->f_next; c1 = &rp1->rp_inet; cend = c1; c1 += (iphlen1 + foff1); cend += (iphlen1 + fend + 1); c2 = &rp2->rp_inet; c2 += (iphlen2 + foff2); /* Free space may only include part of incoming frag */ i1 = c1; i2 = c2; while (i1 < cend) *i1++ = *i2++; fend = fendsv; } if ((rp1->rp_inet.ip_chksum == NULL) || ( (!(rp1->rp_inet.ip_flgs & MOREFRAGS)) && (rp1->rp_inet.ip_chksum->f_begin > rp1->rp_inet.ip_len)) ) { rp1->rp_inet.ip_len += (iphlen1 + 1); rp1->rp_inet.ip_chksum = 0; rp1->rp_flags &= (~N_FFRAG); rp1->rp_len = NETHSIZ + rp1->rp_inet.ip_len; if (rp1->rp_len & 01) rp1->rp_len++; } return(TRUE); } /* * init_fragsz initialize inet_fragsz table */ init_fragsz() { register int i; for (i = 0; i < 256; i++) inet_fragsz[i] = MAXINET; inet_fragsz[16] = CHAMAX; } /* * inn_maxfrag return max. fragsize for this local and * foreign internet host pair * local fragment size computed as follows: * assume local net is 18 * use local subnet and inet_fragsz table to determine max. * frag size for local net/subnet * foreign fragment size computed as follows: * if local net and foreign net are different, max. frag * size is PKTSIZ * if local net and foreign net are same, use foreign * subnet and inet_fragsz table to determine max. frag * size for foreign net/subnet * return smaller of above 2 frag sizes */ inn_maxfrag(lhost, fhost) struct in_name *lhost, *fhost; { register int lfragsz, ffragsz, fragsz; lfragsz = inet_fragsz[lhost->in_snet]; if (fhost->in_net != lhost->in_net) ffragsz = MAXINET; else ffragsz = inet_fragsz[fhost->in_snet]; fragsz = (lfragsz <= ffragsz) ? lfragsz:ffragsz; return(fragsz); } inn_swab(inp) struct inet_hdr *inp; { register short stop; static short ip_swabads[3] = { &0->ip_len, &0->ip_id, (&0->ip_id) + 1 }; stop = sizeof(ip_swabads)/sizeof(ip_swabads[0]); swaba(inp, ip_swabads, stop); }