home *** CD-ROM | disk | FTP | other *** search
- /* Upper half of IP, consisting of send/receive primitives, including
- * fragment reassembly, for higher level protocols.
- * Not needed when running as a standalone gateway.
- * Copyright 1991 Phil Karn, KA9Q
- */
- #include "global.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "internet.h"
- #include "netuser.h"
- #include "iface.h"
- #include "pktdrvr.h"
- #include "ip.h"
- #include "icmp.h"
-
- static struct mbuf *fraghandle __ARGS((struct ip *ip,struct mbuf *bp));
- static void ip_timeout __ARGS((void *arg));
- static void free_reasm __ARGS((struct reasm *rp));
- static void freefrag __ARGS((struct frag *fp));
- static struct reasm *lookup_reasm __ARGS((struct ip *ip));
- static struct reasm *creat_reasm __ARGS((struct ip *ip));
- static struct frag *newfrag __ARGS((int16 offset,int16 last,struct mbuf *bp));
- static void rdrop __ARGS((struct iface *ifp));
-
- struct mib_entry Ip_mib[20] = {
- "", 0,
- "ipForwarding", 1,
- "ipDefaultTTL", MAXTTL,
- "ipInReceives", 0,
- "ipInHdrErrors", 0,
- "ipInAddrErrors", 0,
- "ipForwDatagrams", 0,
- "ipInUnknownProtos", 0,
- "ipInDiscards", 0,
- "ipInDelivers", 0,
- "ipOutRequests", 0,
- "ipOutDiscards", 0,
- "ipOutNoRoutes", 0,
- "ipReasmTimeout", TLB,
- "ipReasmReqds", 0,
- "ipReasmOKs", 0,
- "ipReasmFails", 0,
- "ipFragOKs", 0,
- "ipFragFails", 0,
- "ipFragCreates", 0,
- };
-
- struct reasm *Reasmq;
- static struct raw_ip *Raw_ip;
-
- #define INSERT 0
- #define APPEND 1
- #define PREPEND 2
-
- /* Send an IP datagram. Modeled after the example interface on p 32 of
- * RFC 791
- */
- int
- 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 *tbp;
- struct ip ip; /* IP header */
- static int16 id_cntr = 0; /* Datagram serial number */
-
- ipOutRequests++;
-
- if(source == INADDR_ANY)
- source = locaddr(dest);
- if(length == 0 && bp != NULLBUF)
- length = len_p(bp);
- if(id == 0)
- id = id_cntr++;
- if(ttl == 0)
- ttl = ipDefaultTTL;
-
- /* Fill in IP header */
- ip.version = IPVERSION;
- ip.tos = tos;
- ip.length = IPLEN + length;
- ip.id = id;
- ip.offset = 0;
- ip.flags.mf = 0;
- ip.flags.df = df;
- ip.flags.congest = 0;
- ip.ttl = ttl;
- ip.protocol = protocol;
- ip.source = source;
- ip.dest = dest;
- ip.optlen = 0;
- if((tbp = htonip(&ip,bp,IP_CS_NEW)) == NULLBUF){
- free_p(bp);
- return -1;
- }
- if(ismyaddr(ip.dest)){
- /* Pretend it has been sent by the loopback interface before
- * it appears in the receive queue
- */
- net_route(&Loopback,tbp);
- Loopback.ipsndcnt++;
- Loopback.rawsndcnt++;
- Loopback.lastsent = secclock();
- } else
- net_route(NULLIF,tbp);
- return 0;
- }
-
- /* Reassemble incoming IP fragments and dispatch completed datagrams
- * to the proper transport module
- */
- void
- ip_recv(iface,ip,bp,rxbroadcast)
- struct iface *iface; /* Incoming interface */
- struct ip *ip; /* Extracted IP header */
- struct mbuf *bp; /* Data portion */
- int rxbroadcast; /* True if received on subnet broadcast address */
- {
- /* Function to call with completed datagram */
- register struct raw_ip *rp;
- struct mbuf *bp1,*tbp;
- int rxcnt = 0;
- register struct iplink *ipp;
-
- /* If we have a complete packet, call the next layer
- * to handle the result. Note that fraghandle passes back
- * a length field that does NOT include the IP header
- */
- if((bp = fraghandle(ip,bp)) == NULLBUF)
- return; /* Not done yet */
-
- ipInDelivers++;
-
- for(rp = Raw_ip;rp != NULLRIP;rp = rp->next){
- if(rp->protocol != ip->protocol)
- continue;
- rxcnt++;
- /* Duplicate the data portion, and put the header back on */
- dup_p(&bp1,bp,0,len_p(bp));
- if(bp1 != NULLBUF && (tbp = htonip(ip,bp1,IP_CS_OLD)) != NULLBUF){
- enqueue(&rp->rcvq,tbp);
- if(rp->r_upcall != NULLVFP)
- (*rp->r_upcall)(rp);
- } else {
- free_p(bp1);
- }
- }
- /* Look it up in the transport protocol table */
- for(ipp = Iplink;ipp->funct != NULL;ipp++){
- if(ipp->proto == ip->protocol)
- break;
- }
- if(ipp->funct != NULL){
- /* Found, call transport protocol */
- (*ipp->funct)(iface,ip,bp,rxbroadcast);
- } else {
- /* Not found */
- if(rxcnt == 0){
- /* Send an ICMP Protocol Unknown response... */
- ipInUnknownProtos++;
- /* ...unless it's a broadcast */
- if(!rxbroadcast){
- icmp_output(ip,bp,ICMP_DEST_UNREACH,
- ICMP_PROT_UNREACH,NULLICMP);
- }
- }
- free_p(bp);
- }
- }
- /* Handle IP packets encapsulated inside IP */
- void
- ipip_recv(iface,ip,bp,rxbroadcast)
- struct iface *iface; /* Incoming interface */
- struct ip *ip; /* Extracted IP header */
- struct mbuf *bp; /* Data portion */
- int rxbroadcast; /* True if received on subnet broadcast address */
- {
- net_route(&Encap,bp);
- }
-
- /* Process IP datagram fragments
- * If datagram is complete, return it with ip->length containing the data
- * length (MINUS header); otherwise return NULLBUF
- */
- static
- struct mbuf *
- fraghandle(ip,bp)
- struct ip *ip; /* IP header, host byte order */
- struct mbuf *bp; /* The fragment itself */
- {
- register struct reasm *rp; /* Pointer to reassembly descriptor */
- struct frag *lastfrag,*nextfrag,*tfp;
- struct mbuf *tbp;
- int16 i;
- int16 last; /* Index of first byte beyond fragment */
-
- last = ip->offset + ip->length - (IPLEN + ip->optlen);
-
- rp = lookup_reasm(ip);
- if(ip->offset == 0 && !ip->flags.mf){
- /* Complete datagram received. Discard any earlier fragments */
- if(rp != NULLREASM){
- free_reasm(rp);
- ipReasmOKs++;
- }
- return bp;
- }
- ipReasmReqds++;
- if(rp == NULLREASM){
- /* First fragment; create new reassembly descriptor */
- if((rp = creat_reasm(ip)) == NULLREASM){
- /* No space for descriptor, drop fragment */
- ipReasmFails++;
- 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(!ip->flags.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 > ip->offset)
- break;
- lastfrag = nextfrag;
- }
- /* Check for overlap with preceeding fragment */
- if(lastfrag != NULLFRAG && ip->offset < lastfrag->last){
- /* Strip overlap from new fragment */
- i = lastfrag->last - ip->offset;
- pullup(&bp,NULLCHAR,i);
- if(bp == NULLBUF)
- return NULLBUF; /* Nothing left */
- ip->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 == ip->offset)
- i |= APPEND;
- if(nextfrag != NULLFRAG && nextfrag->offset == last)
- i |= PREPEND;
- switch(i){
- case INSERT: /* Insert new desc between lastfrag and nextfrag */
- tfp = newfrag(ip->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 = ip->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;
- /* Tell IP the entire length */
- ip->length = rp->length + (IPLEN + ip->optlen);
- free_reasm(rp);
- ipReasmOKs++;
- return bp;
- } else
- return NULLBUF;
- }
- /* Arrange for receipt of raw IP datagrams */
- struct raw_ip *
- raw_ip(protocol,r_upcall)
- int protocol;
- void (*r_upcall)();
- {
- register struct raw_ip *rp;
-
- rp = (struct raw_ip *)callocw(1,sizeof(struct raw_ip));
- rp->protocol = protocol;
- rp->r_upcall = r_upcall;
- rp->next = Raw_ip;
- Raw_ip = rp;
- return rp;
- }
- /* Free a raw IP descriptor */
- void
- del_ip(rpp)
- struct raw_ip *rpp;
- {
- struct raw_ip *rplast = NULLRIP;
- register struct raw_ip *rp;
-
- /* Do sanity check on arg */
- for(rp = Raw_ip;rp != NULLRIP;rplast=rp,rp = rp->next)
- if(rp == rpp)
- break;
- if(rp == NULLRIP)
- return; /* Doesn't exist */
-
- /* Unlink */
- if(rplast != NULLRIP)
- rplast->next = rp->next;
- else
- Raw_ip = rp->next;
- /* Free resources */
- free_q(&rp->rcvq);
- free((char *)rp);
- }
-
- static struct reasm *
- lookup_reasm(ip)
- struct ip *ip;
- {
- register struct reasm *rp;
- struct reasm *rplast = NULLREASM;
-
- for(rp = Reasmq;rp != NULLREASM;rplast=rp,rp = rp->next){
- if(ip->id == rp->id && ip->source == rp->source
- && ip->dest == rp->dest && ip->protocol == rp->protocol){
- if(rplast != NULLREASM){
- /* Move to top of list for speed */
- rplast->next = rp->next;
- rp->next = Reasmq;
- Reasmq = rp;
- }
- return rp;
- }
-
- }
- return NULLREASM;
- }
- /* Create a reassembly descriptor,
- * put at head of reassembly list
- */
- static struct reasm *
- creat_reasm(ip)
- register struct ip *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;
- set_timer(&rp->timer,ipReasmTimeout * 1000L);
- rp->timer.func = ip_timeout;
- rp->timer.arg = rp;
-
- rp->next = Reasmq;
- Reasmq = rp;
- return rp;
- }
-
- /* Free all resources associated with a reassembly descriptor */
- static void
- free_reasm(r)
- struct reasm *r;
- {
- register struct reasm *rp;
- struct reasm *rplast = NULLREASM;
- register struct frag *fp;
-
- for(rp = Reasmq;rp != NULLREASM;rplast = rp,rp=rp->next)
- if(r == rp)
- break;
- if(rp == NULLREASM)
- return; /* Not on list */
-
- stop_timer(&rp->timer);
- /* Remove from list of reassembly descriptors */
- if(rplast != NULLREASM)
- rplast->next = rp->next;
- else
- Reasmq = rp->next;
-
- /* 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)
- void *arg;
- {
- register struct reasm *rp;
-
- rp = (struct reasm *)arg;
- free_reasm(rp);
- ipReasmFails++;
- }
- /* 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);
- }
-
- /* In red alert mode, blow away the whole reassembly queue. Otherwise crunch
- * each fragment on each reassembly descriptor
- */
- void
- ip_garbage(red)
- int red;
- {
- struct reasm *rp,*rp1;
- struct frag *fp;
- struct raw_ip *rwp;
- struct iface *ifp;
-
- /* Run through the reassembly queue */
- for(rp = Reasmq;rp != NULLREASM;rp = rp1){
- rp1 = rp->next;
- if(red){
- free_reasm(rp);
- } else {
- for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){
- mbuf_crunch(&fp->buf);
- }
- }
- }
- /* Run through the raw IP queue */
- for(rwp = Raw_ip;rwp != NULLRIP;rwp = rwp->next)
- mbuf_crunch(&rwp->rcvq);
- /* In red mode, drop one packet on each interface output queue */
- if(!red)
- return;
- for(ifp=Ifaces;ifp != NULLIF;ifp = ifp->next){
- rdrop(ifp);
- }
- }
- /* Execute random drop algorithm on an interface's output queue */
- static void
- rdrop(ifp)
- struct iface *ifp;
- {
- struct mbuf *bp,*bplast;
- int i;
-
- i = len_q(ifp->outq);
- if(i == 0)
- return; /* Queue is empty */
-
- i = random(i); /* Select condemned party */
-
- /* Search for i-th message on queue */
- bplast = NULLBUF;
- for(bp = ifp->outq;bp != NULLBUF && i>0;i--,bplast=bp,bp=bp->anext)
- ;
- /* Now remove and free it */
- if(bplast != NULLBUF)
- bplast->anext = bp->anext;
- else
- ifp->outq = bp->anext; /* First on list */
- free_p(bp);
- }
-