/* Upper half of IP, consisting of send/receive primitives, including * fragment reassembly, for higher level protocols. * Not needed when running as a standalone gateway. */ #define TLB 30 /* Reassembly limit time */ #include #include "machdep.h" #include "mbuf.h" #include "timer.h" #include "internet.h" #include "ip.h" #include "icmp.h" #include "iface.h" int ip_recv(); /* Should be void, but C complains */ char *calloc(),*malloc(); char ip_ttl = MAXTTL; /* Default time-to-live for IP datagrams */ struct reasm *reasmq; #define INSERT 0 #define APPEND 1 #define PREPEND 2 /* Send an IP datagram. Modeled after the example interface on p 32 of * RFC 791 */ void ip_send(source,dest,protocol,tos,ttl,bp,length,id,df) int32 source; /* source address */ int32 dest; /* Destination address */ char protocol; /* Protocol */ char tos; /* Type of service */ char ttl; /* Time-to-live */ struct mbuf *bp; /* Data portion of datagram */ int16 length; /* Optional length of data portion */ int16 id; /* Optional identification */ char df; /* Don't-fragment flag */ { struct mbuf *hbp; /* mbuf containing IP header */ struct ip_header *iph; /* Pointer to IP header */ static int16 id_cntr; /* Datagram serial number */ int16 hdr_len; /* IP header length, bytes */ void ip_route(); /* Datagram router */ if(length == 0 && bp != NULLBUF) length = len_mbuf(bp); if(id == 0) id = id_cntr++; if(ttl == 0) ttl = ip_ttl; /* Allocate an mbuf for the IP header */ hdr_len = sizeof(struct ip_header); if((hbp = alloc_mbuf(hdr_len)) == NULLBUF){ /* We really ought to source-quench the sender, but that would * probably fail too. */ free_p(bp); return; } hbp->cnt = hdr_len; /* and fill it in */ iph = (struct ip_header *)hbp->data; iph->v_ihl = (IPVERSION << 4) | (hdr_len/sizeof(int32)); iph->tos = tos; iph->length = htons(hdr_len + length); iph->id = htons(id); if(df) iph->fl_offs = htons(DF); else iph->fl_offs = 0; iph->ttl = ttl; iph->protocol = protocol; iph->checksum = 0; iph->source = htonl(source); iph->dest = htonl(dest); iph->checksum = cksum(NULLHEADER,hbp,hdr_len); hbp->next = bp; ip_route(hbp,0); /* Toss it to the router */ } /* Reassemble incoming IP fragments and dispatch completed datagrams * to the proper transport module */ int /* Should really be void */ ip_recv(bp,rxbroadcast) struct mbuf *bp; char rxbroadcast; { register struct ip_header *ipp; /* Pointer to original IP header */ struct ip_header ip; /* Extracted copy of header */ int16 ip_len; /* Length of IP header */ struct mbuf *fraghandle(); void (*recv)(); /* Function to call with completed datagram */ void tcp_input(),udp_input(),icmp_input(); ipp = (struct ip_header *)bp->data; /* Initial check for protocols we can't handle */ switch(ipp->protocol & 0xff){ case TCP_PTCL: recv = tcp_input; break; case UDP_PTCL: recv = udp_input; break; case ICMP_PTCL: recv = icmp_input; break; default: /* Send an ICMP Protocol Unknown response... */ ip_stats.badproto++; /* ...unless it's a broadcast */ if(!rxbroadcast) icmp_output(bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL); free_p(bp); return; } ip_len = lonibble(ipp->v_ihl) * sizeof(int32); /* Extract IP header */ pullup(&bp,(char *)&ip,ip_len); /* Convert to host byte order */ ip.length = ntohs(ip.length) - ip_len; /* Length of data portion */ ip.id = ntohs(ip.id); ip.fl_offs = ntohs(ip.fl_offs); ip.source = ntohl(ip.source); ip.dest = ntohl(ip.dest); /* If we have a complete packet, call the next layer * to handle the result */ if((bp = fraghandle(&ip,bp)) != NULLBUF) (*recv)(bp,ip.protocol,ip.source,ip.dest,ip.tos,ip.length,rxbroadcast); } /* Process IP datagram fragments * If datagram is complete, return it with ip->length containing its * entire length; otherwise return NULLBUF */ static struct mbuf * fraghandle(ip,bp) struct ip_header *ip; /* IP header, host byte order */ struct mbuf *bp; /* The fragment itself */ { void ip_timeout(),freefrag(),free_reasm(); struct reasm *lookup_reasm(),*creat_reasm(); register struct reasm *rp; /* Pointer to reassembly descriptor */ struct frag *lastfrag,*nextfrag,*tfp,*newfrag(); struct mbuf *tbp; int16 i; int16 offset; /* Index of first byte in fragment */ int16 last; /* Index of first byte beyond fragment */ char mf; /* 1 if not last fragment, 0 otherwise */ offset = (ip->fl_offs & F_OFFSET) << 3; /* Convert to bytes */ last = offset + ip->length; mf = (ip->fl_offs & MF) ? 1 : 0; rp = lookup_reasm(ip); if(offset == 0 && !mf){ /* Complete datagram received. Discard any earlier fragments */ if(rp != NULLREASM) free_reasm(rp); return bp; } if(rp == NULLREASM){ /* First fragment; create new reassembly descriptor */ if((rp = creat_reasm(ip)) == NULLREASM){ /* No space for descriptor, drop fragment */ free_p(bp); return NULLBUF; } } /* Keep restarting timer as long as we keep getting fragments */ stop_timer(&rp->timer); start_timer(&rp->timer); /* If this is the last fragment, we now know how long the * entire datagram is; record it */ if(!mf) rp->length = last; /* Set nextfrag to the first fragment which begins after us, * and lastfrag to the last fragment which begins before us */ lastfrag = NULLFRAG; for(nextfrag = rp->fraglist;nextfrag != NULLFRAG;nextfrag = nextfrag->next){ if(nextfrag->offset > offset) break; lastfrag = nextfrag; } /* Check for overlap with preceeding fragment */ if(lastfrag != NULLFRAG && offset < lastfrag->last){ /* Strip overlap from new fragment */ i = lastfrag->last - offset; pullup(&bp,NULLCHAR,i); if(bp == NULLBUF) return NULLBUF; /* Nothing left */ offset += i; } /* Look for overlap with succeeding segments */ for(; nextfrag != NULLFRAG; nextfrag = tfp){ tfp = nextfrag->next; /* save in case we delete fp */ if(nextfrag->offset >= last) break; /* Past our end */ /* Trim the front of this entry; if nothing is * left, remove it. */ i = last - nextfrag->offset; pullup(&nextfrag->buf,NULLCHAR,i); if(nextfrag->buf == NULLBUF){ /* superseded; delete from list */ if(nextfrag->prev != NULLFRAG) nextfrag->prev->next = nextfrag->next; else rp->fraglist = nextfrag->next; if(tfp->next != NULLFRAG) nextfrag->next->prev = nextfrag->prev; freefrag(nextfrag); } else nextfrag->offset = last; } /* Lastfrag now points, as before, to the fragment before us; * nextfrag points at the next fragment. Check to see if we can * join to either or both fragments. */ i = INSERT; if(lastfrag != NULLFRAG && lastfrag->last == offset) i |= APPEND; if(nextfrag != NULLFRAG && nextfrag->offset == last) i |= PREPEND; switch(i){ case INSERT: /* Insert new desc between lastfrag and nextfrag */ tfp = newfrag(offset,last,bp); tfp->prev = lastfrag; tfp->next = nextfrag; if(lastfrag != NULLFRAG) lastfrag->next = tfp; /* Middle of list */ else rp->fraglist = tfp; /* First on list */ if(nextfrag != NULLFRAG) nextfrag->prev = tfp; break; case APPEND: /* Append to lastfrag */ append(&lastfrag->buf,bp); lastfrag->last = last; /* Extend forward */ break; case PREPEND: /* Prepend to nextfrag */ tbp = nextfrag->buf; nextfrag->buf = bp; append(&nextfrag->buf,tbp); nextfrag->offset = offset; /* Extend backward */ break; case (APPEND|PREPEND): /* Consolidate by appending this fragment and nextfrag * to lastfrag and removing the nextfrag descriptor */ append(&lastfrag->buf,bp); append(&lastfrag->buf,nextfrag->buf); nextfrag->buf = NULLBUF; lastfrag->last = nextfrag->last; /* Finally unlink and delete the now unneeded nextfrag */ lastfrag->next = nextfrag->next; if(nextfrag->next != NULLFRAG) nextfrag->next->prev = lastfrag; freefrag(nextfrag); break; } if(rp->fraglist->offset == 0 && rp->fraglist->next == NULLFRAG && rp->length != 0){ /* We've gotten a complete datagram, so extract it from the * reassembly buffer and pass it on. */ bp = rp->fraglist->buf; rp->fraglist->buf = NULLBUF; ip->length = rp->length; /* Tell IP the entire length */ free_reasm(rp); return bp; } else return NULLBUF; } static struct reasm * lookup_reasm(ip) struct ip_header *ip; { register struct reasm *rp; for(rp = reasmq;rp != NULLREASM;rp = rp->next){ if(ip->source == rp->source && ip->dest == rp->dest && ip->protocol == rp->protocol && ip->id == rp->id) return rp; } return NULLREASM; } #ifdef FOO static int16 hash_reasm(source,dest,protocol,id) int32 source; int32 dest, char protocol; int16 id; { register int16 hval; hval = loword(source); hval ^= hiword(source); hval ^= loword(dest); hval ^= hiword(dest); hval ^= protocol & 0xff; hval ^= id; hval %= RHASH; return hval; } #endif /* Create a reassembly descriptor, * put at head of reassembly list */ static struct reasm * creat_reasm(ip) register struct ip_header *ip; { register struct reasm *rp; if((rp = (struct reasm *)calloc(1,sizeof(struct reasm))) == NULLREASM) return rp; /* No space for descriptor */ rp->source = ip->source; rp->dest = ip->dest; rp->id = ip->id; rp->protocol = ip->protocol; rp->timer.start = TLB; rp->timer.func = ip_timeout; rp->timer.arg = (int *)rp; rp->next = reasmq; if(rp->next != NULLREASM) rp->next->prev = rp; reasmq = rp; return rp; } /* Free all resources associated with a reassembly descriptor */ static void free_reasm(rp) register struct reasm *rp; { register struct frag *fp; stop_timer(&rp->timer); /* Remove from list of reassembly descriptors */ if(rp->prev != NULLREASM) rp->prev->next = rp->next; else reasmq = rp->next; if(rp->next != NULLREASM) rp->next->prev = rp->prev; /* Free any fragments on list, starting at beginning */ while((fp = rp->fraglist) != NULLFRAG){ rp->fraglist = fp->next; free_p(fp->buf); free((char *)fp); } free((char *)rp); } /* Handle reassembly timeouts by deleting all reassembly resources */ static void ip_timeout(arg) int *arg; { register struct reasm *rp; rp = (struct reasm *)arg; free_reasm(rp); } /* Create a fragment */ static struct frag * newfrag(offset,last,bp) int16 offset,last; struct mbuf *bp; { struct frag *fp; if((fp = (struct frag *)calloc(1,sizeof(struct frag))) == NULLFRAG){ /* Drop fragment */ free_p(bp); return NULLFRAG; } fp->buf = bp; fp->offset = offset; fp->last = last; return fp; } /* Delete a fragment, return next one on queue */ static void freefrag(fp) struct frag *fp; { free_p(fp->buf); free((char *)fp); } #ifdef TRACE int doipstat(argc,argv) int argc; char *argv[]; { extern struct ip_stats ip_stats; extern struct reasm *reasmq; register struct reasm *rp; register struct frag *fp; char *inet_ntoa(); printf("total %ld runt %u len err %u vers err %u", ip_stats.total,ip_stats.runt,ip_stats.length,ip_stats.version); printf(" chksum err %u badproto %u\r\n", ip_stats.checksum,ip_stats.badproto); if(reasmq != NULLREASM) printf("Reassembly fragments:\r\n"); for(rp = reasmq;rp != NULLREASM;rp = rp->next){ printf("src %s",inet_ntoa(rp->source)); printf(" dest %s",inet_ntoa(rp->dest)); printf(" id %u pctl %u time %u len %u\r\n", rp->id,rp->protocol,rp->timer.count,rp->length); for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){ printf(" offset %u last %u\r\n",fp->offset,fp->last); } } return 0; } #endif