V8/usr/sys/chaosld/chrouteld.c
/*
* 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