V8/usr/sys/chaosld/chrouteld.c

Compare this file to the similar file:
Show the results in this format:

/*
 *             C H R O U T E  L D
 *
 * Chaosnet packet routing line discipline.
 *
 *
 * (c) Copyright 1985  Nirvonics, Inc.
 *
 * Written by Kurt Gollhardt
 * Last update Tue Mar 12 04:21:35 1985
 *
 * This software is the property of Nirvonics, Inc.
 * All rights reserved.
 *
 */

#include "chroute.h"
#if NCHROUTE > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/stream.h"
#include "../h/ioctl.h"
#include "../h/ttyld.h"
#include "../h/conf.h"
#include "../h/order.h"
#include "../h/ethernet.h"
#include "../chaosld/types.h"
#include "../chaosld/constants.h"
#include "../chaosld/chrouteld.h"


struct chif    Chif[NCHROUTE];
struct chroute Chroute[CHNSUBNET];

static struct chif  *primary;

int  NChropen;
int  NChroute = NCHROUTE;
int  NChsubnet = CHNSUBNET;

int  Chrtimer;	/* Routing timer running */

int chroute_open(), chroute_close(), chroute_put(), chroute_srv();
static struct qinit chroute_init = {
          chroute_put, chroute_srv, chroute_open, chroute_close, 512, 64
};
struct streamtab chrouteinfo = { &chroute_init, &chroute_init };

#define DEBUG

#ifdef DEBUG
     int  Chrdebug = 0;
#    define debug(x)     if (Chrdebug) x
#else
#    define debug(x)
#endif

static struct statxcvr	 zstat;


chroute_open(q, dev)
     register struct queue    *q;
     dev_t     dev;
{
     register int   i;
     register struct chif     *ifp;

     if (q->ptr)    /* If this stream is already open, don't do anything */
          return 1;

     /* Look for a free interface structure */

     if (NChropen++ == NCHROUTE)
	  return 0;  /* If there are none left, the open fails */
     for (i = 0, ifp = Chif; ifp->rdq != 0; ++ifp)
          ;

     ifp->rdq = q;
     ifp->my_addr.ch_addr = 0;
     ifp->arp = -1;
     ifp->if_dev = dev;
     ifp->if_cost = HIGH_COST / 4;  /* We expect this to be set by an ioctl,
                                        but just in case ... */
     ifp->if_stat = zstat;

     q->flag |= QDELIM|QNOENB;
     WR(q)->flag |= QDELIM|QNOENB;
     q->ptr = WR(q)->ptr = (caddr_t)ifp;

     if (!Chrtimer)
	  ch_rtimer();

     return 1;
}


chroute_close(q)
     register struct queue    *q;
{
     register struct chif     *ifp;

     --NChropen;
     ifp = (struct chif *)q->ptr;
     Chroute[ifp->my_addr.ch_subnet].rt_type = CHNOPATH;
     arp_disable(ifp->arp);
     ifp->rdq = (struct queue *)0;
     ifp->my_addr.ch_addr = 0;
     if (ifp == primary)
          primary = (struct chif *)0;
     q->ptr = 0;
}


chroute_put(q, bp)
     register struct queue    *q;
     register struct block    *bp;
{
     debug(printf("chroute_put(%s, %d)\n",
                    ((q->flag & QREADR)? "RD" : "WR"), bp->type));

     switch (bp->type) {
          case M_DATA:
	       putq(q, bp);
	       break;
          case M_DELIM:
	       putq(q, bp);
	       qenable(q);
	       break;
          case M_IOCTL:
	       /* Process an ioctl only if it's going towards the device */
	       if (!(q->flag & QREADR) && chroute_ioctl(q, bp))
	            break;
          default:  /* Anything else, just pass on to the next guy */
	       (*q->next->qinfo->putp)(q->next, bp);
     }
}


chroute_srv(q)
     register struct queue    *q;
{
     register struct block    *bp;
     register struct chif     *ifp;
     register struct chroute  *r;
     int       minsize;

     /* We have a complete packet in the queue, composed of some number
          of M_DATA's followed by an M_DELIM.
        We know there are no other block types, because chroute_put()
	  won't give us anything else.  */

     debug(printf("chroute_srv(%s)\n", ((q->flag & QREADR)? "RD" : "WR")));

     if (bp = q->first) {
          if (bp->type == M_DELIM) {
	       printf("chroute: delimiter without data\n");
	       flush_packet(q);
	       return;
          }
	  minsize = sizeof(struct pkt_header);
	  if (q->flag & QREADR) {
	       /* The packet is coming from the device */
	       minsize += sizeof(struct ether_in);
          }
          if (block_pullup(q, minsize) == 0) {
	       flush_packet(q);
	       return;
          }
	  if (q->flag & QREADR) {
	       /* Packet is from device; strip off ethernet header */
	       q->first->rptr += sizeof(struct ether_in);
	       ifp = (struct chif *)q->ptr;
	       ifp->ist_rcvd++;
	       if (ifp->my_addr.ch_addr != 0) {
		    r = &Chroute[ifp->my_addr.ch_subnet];
		    r->rt_type = CHDIRECT;
		    r->rt_cost = ifp->if_cost;
		    r->rt_path.ifp = ifp;
	       }
          }
	  chroute_dispatch(q);

          /* See if there's another packet on the queue */
	  for (bp = q->first; bp != NOBLOCK; bp = bp->next)
	       if (bp->type == M_DELIM) {
	            qenable(q);    /* re-enable queue for the next packet */
		    break;
               }
     }
}


     /* Handle an ioctl */

chroute_ioctl(q, bp)
     register struct queue    *q;
     register struct block    *bp;
{
     register union stmsg     *sp;
     register struct chif     *ifp;
     register struct chroute  *rt;
     register struct chaddr_pair   *chp;

     ifp = (struct chif *)q->ptr;
     sp = (union stmsg *)bp->rptr;
     switch (sp->iocx.com) {
          case CHIOCADDR:
	       bp->wptr = bp->rptr;
	       if (ifp->my_addr.ch_addr != 0) {
	            Chroute[ifp->my_addr.ch_subnet].rt_type = CHNOPATH;
		    arp_disable(ifp->arp);
               }
	       ifp->my_addr.ch_addr = *(int *)(sp->iocx.xxx);
	       if (ifp->my_addr.ch_subnet >= CHNSUBNET
	                 || ifp->my_addr.ch_subnet == 0) {
	            ifp->my_addr.ch_addr = 0;
		    bp->type = M_IOCNAK;
		    break;
               }
	       rt = &Chroute[ifp->my_addr.ch_subnet];
	       rt->rt_type = CHDIRECT;
	       rt->rt_cost = ifp->if_cost;
	       rt->rt_path.ifp = ifp;
               /* Should we check if this fails? */
	       ifp->arp = arp_enable(ifp->if_dev, ETHERPUP_CHAOSTYPE,
	                      sizeof(chaddr), 0, &ifp->my_addr);
               bp->type = M_IOCACK;
	       break;
          case CRIOCOST:
	       ifp->if_cost = *(u_short *)(sp->iocx.xxx);
	       bp->wptr = bp->rptr;
	       bp->type = M_IOCACK;
	       break;
          case CRIOPRIMARY:
	       primary = ifp;
	       bp->wptr = bp->rptr;
	       bp->type = M_IOCACK;
	       break;
          default:
	       return 0;   /* Let someone else deal with it */
     }

     qreply(q, bp);
     return 1;
}


     /* We've got a packet to send (or receive); send it to the right place */

chroute_dispatch(q)
     register struct queue    *q;
{
     register struct block    *bp;
     register struct pkt_header *ph;
     register struct chroute  *rt;
     register struct chif     *ifp;
     register int        len, isdelim;
     struct ether_out    *ep;
     chaddr              dest;

     ph = (struct pkt_header *)q->first->rptr;
     ifp = (struct chif *)q->ptr;
     debug(printf("chroute_dispatch: op 0%o len %d\n", ph->ph_op, ph->ph_len));

     if (q->flag & QREADR && ph->ph_op == RUTOP) {
          if (block_pullup(q, ph->ph_len + sizeof(*ph)) == 0) {
	       ifp->ist_rej++;
	       flush_packet(q);
	       return;
          }
          chroute_rcvrut(q->first->rptr);
	  flush_packet(q);
	  return;
     }

     if (primary && (ph->ph_daddr.ch_addr == primary->my_addr.ch_addr
                      || (q->flag & QREADR && ph->ph_daddr.ch_addr == 0))) {
          ph->ph_daddr = primary->my_addr;
          if (ph->ph_len > CHMAXDATA) {
	       ifp->ist_len++;
	       flush_packet(q);
	       return;
          }
	  len = sizeof(struct pkt_header) + ph->ph_len;
	  while (bp = getq(q)) {
	       if (bp->type == M_DATA) {
	            if (bp->wptr - bp->rptr > len)
		         bp->wptr = bp->rptr + len;
                    len -= bp->wptr - bp->rptr;
		    if (bp->wptr <= bp->rptr) {
		         freeb(bp);
			 continue;
		    }
	       }
               debug(print_buf(bp, "rcvd:"));
               isdelim = (bp->type == M_DELIM);
	       (*primary->rdq->next->qinfo->putp)(primary->rdq->next, bp);
	       if (isdelim)
	            break;
          }
	  if (len != 0)
	       ifp->ist_len++;
          return;
     }

     if (q->flag & QREADR) {
          if (ph->ph_daddr.ch_host == 0) {
	       ifp->ist_rej++;
	       flush_packet(q);
	       return;
          } else if (++ph->ph_fc == 0) {
               /* Overforwarded packet */
	       flush_packet(q);
	       return;
          }
     }

     dest = ph->ph_daddr;
     if (ph->ph_daddr.ch_host == 0)
          ph->ph_daddr.ch_subnet = 0;
     if (dest.ch_subnet >= CHNSUBNET
            || (rt = &Chroute[dest.ch_subnet])->rt_type == CHNOPATH) {
	  flush_packet(q);
	  return;
     }

     if (rt->rt_type == CHFIXED || rt->rt_type == CHBRIDGE)
          dest = rt->rt_path.bridge;
     ifp = Chroute[dest.ch_subnet].rt_path.ifp;

     if ((bp = allocb(sizeof(struct ether_out))) == 0) {
          ifp->ist_abrt++;
	  flush_packet(q);
	  return;
     }
     ep = (struct ether_out *)bp->wptr;
     bp->wptr += sizeof(struct ether_out);
     ep->type = hfirst_short(ETHERPUP_CHAOSTYPE);

     if (dest.ch_host == 0) /* If address is 0, broadcast to everyone */
          arp_broadcast(ep->dhost);
     else if (arp_getaddr(ifp->arp, &dest, ep->dhost) == -1) {
          ifp->ist_abrt++;
          flush_packet(q);
	  return;
     }

     do {
          debug(print_buf(bp, "xmit:"));
          isdelim = (bp->type == M_DELIM);
          (*(WR(ifp->rdq)->next->qinfo->putp)) (WR(ifp->rdq)->next, bp);
          if (isdelim)
               break;
     } while (bp = getq(q));

     ifp->ist_xmit++;
}


     /* Handle RUT (Routing) Packets */
chroute_rcvrut(ph)
     register struct pkt_header    *ph;
{
     register struct rut_data      *rd;
     register struct chroute       *rt;
     register int   i;

     if ((i = ph->ph_saddr.ch_subnet) >= CHNSUBNET) {
          printf("CHAOS: Bad subnet (%d) in RUT packet\n", i);
	  return;
     }
     if (Chroute[i].rt_type != CHDIRECT) {
          printf("CHAOS: RUT packet from unconnected subnet %d\n", i);
	  return;
     }

     debug(printf("chroute_rcvrut(%d)\n", i));

     rd = (struct rut_data *)((char *)ph + sizeof(struct pkt_header));
     for (i = ph->ph_len; i > 0; i -= sizeof(struct rut_data), ++rd) {
          if (rd->rd_subnet >= CHNSUBNET)
	       continue;
	  rt = &Chroute[rd->rd_subnet];
	  switch (rt->rt_type) {
	       case CHBRIDGE:
	            if (rd->rd_cost > rt->rt_cost)
		         break;
	       case CHNOPATH:
                    rt->rt_cost = rd->rd_cost;
		    rt->rt_type = CHBRIDGE;
		    rt->rt_path.bridge = ph->ph_saddr;
		    break;
               case CHDIRECT:
	       case CHFIXED:
	            break;
               default:
	            printf("CHAOS: Bad entry in routing table, type = %d\n",
		              rt->rt_type);
          }
     }
}


#ifdef DEBUG

print_buf(bp, s)
     register struct block *bp;
     register char *s;
{
     register u_char *p;
     register int   i;

     printf("%s msg type %d len %d", s, bp->type, bp->wptr - bp->rptr);
     for (i = 0, p = bp->rptr; p < bp->wptr; ++i, ++p) {
          if ((i & 31) == 0)
	       printf("\n%s", s);
          printf(" %x", *p);
     }
     printf("\n");
}

#endif DEBUG