V9/sys/net/arpld.c

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

/*
 *             A R P  L D
 *
 * Generic (ethernet) address-resolution line discipline.
 * Implements an LRU cache of protocol address to ethernet address mappings.
 *
 *
 * Written by Kurt Gollhardt  (Nirvonics, Inc.)
 * Last update Wed Feb 12 20:43:28 1986
 *
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/stream.h"
#include "../h/conf.h"
#include "../h/ioctl.h"
#include "../h/ttyld.h"
#include "../h/ethernet.h"
#include "../h/order.h"
#include "../net/arpld.h"

#include "arp.h"
#if NARP > 0

#define NARPROTO  (NARP * NPROTO)

struct arp_dev Arp_dev[NARP];
struct proto   Arp_proto[NARPROTO];

static int     hd_size = MAX_HARDSIZE,
               pr_size, pr_hfirst;

static u_char  hd_sender[MAX_HARDSIZE],
               hd_target[MAX_HARDSIZE],
	       pr_sender[MAX_PRSIZE],
	       pr_target[MAX_PRSIZE];

static unsigned	    arp_clock;

static int install(), request();
static int pack(), unpack();
static int bcmp();

struct block *packet_pullup();

#define DEBUG

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


#define order(x,size,hfirst) ((hfirst) ? order_hfirst(x,size) \
                                       : order_lfirst(x,size))


arp_enable(dev, type, psize, phfirst, paddr)
     dev_t     dev;
     u_char    *paddr;
{
     register struct proto    *pr, *pr2;
     register struct arp_dev  *arpd;
     register struct arp      *arp;
     register int        i;
     int       ps = spl6();

     dev = physical(dev);

          /* Find the per-device structure for the desired device */
     for (arpd = Arp_dev, i = 0; i < NARP; ++i, ++arpd) {
          if (arpd->pdev == dev)
	       break;
     }
     if (i == NARP) {
          printf("ARP: no arp for dev (%d,%d)\n", major(dev), minor(dev));
	  goto bad;
     }
     if (bcmp(arpd->hdaddr, hzero, MAX_HARDSIZE)) {
	  printf("ARP: hardware address not set for dev (%d,%d)\n",
			 major(dev), minor(dev));
	  goto bad;
     }

          /* Find a free protocol structure for this device */
     pr = 0;
     for (pr2 = &Arp_proto[NPROTO * i], i = 0; i < NPROTO; ++i, ++pr2) {
          if (pr2->psize == 0)
	       pr = pr2;
          else if (pr2->type == type) {
	       printf("ARP: duplicate enable - dev (%d,%d) type 0x%x\n",
	                 major(dev), minor(dev), type);
               goto bad;
          }
     }
     if (pr == 0) {
          printf("ARP: too many protocols on dev (%d,%d)\n",
	            major(dev), minor(dev));
          goto bad;
     }

     if (psize < 1 || psize > MAX_PRSIZE) {
          printf("ARP: protocol address size (%d) out of range\n", psize);
	  goto bad;
     }
     pr->ptr = arpd;
     pr->type = type;
     pr->psize = psize;
     pr->phfirst = phfirst;

          /* Clear out the arp pairs */
     for (arp = pr->pair; arp < &pr->pair[NPAIR]; ++arp)
          bcopy(pzero, arp->paddr, psize);

          /* Add our address as arp pair 0 */
     bcopy(paddr, pr->pair[0].paddr, pr->psize);
     bcopy(arpd->hdaddr, pr->pair[0].hdaddr, hd_size);

     splx(ps);

     debug(printf("ARP: enabled protocol 0x%x on dev (%d,%d), channel %d\n",
               type, major(dev), minor(dev), pr - Arp_proto));
     debug(printf("     my addr is"));
     debug(print_addr(paddr, pr->psize));
     debug(printf(" at ethernet"));
     debug(print_addr(arpd->hdaddr, hd_size));
     debug(printf("  order is %s-byte first\n", phfirst? "high" : "low"));

     return pr - Arp_proto;

bad:
     splx(ps);
     return -1;
}

arp_getaddr(i, paddr, hdaddr)
     u_short   i;
     u_char    *paddr, *hdaddr;
{
     register struct proto    *pr = &Arp_proto[i];
     register struct arp      *arp;
     int       ps;

     if (i >= NARPROTO || pr->psize == 0) {
          printf("ARP: getaddr on un-enabled channel %d\n", i);
	  return -1;
     }

     debug(printf("ARP: getaddr for"));
     debug(print_addr(paddr, pr->psize));
     debug(printf(" on channel %d\n", i));

     ps = spl6();

     for (arp = pr->pair; arp < &pr->pair[NPAIR]; ++arp) {
          if (bcmp(arp->paddr, paddr, pr->psize)) {
	       bcopy(arp->hdaddr, hdaddr, hd_size);
	       arp->time = ++arp_clock;
	       splx(ps);
	       return 0;
          }
     }

     request(pr, paddr);
     splx(ps);
     return -1;
}

struct block *
arp_resolve(i, bp, paddr)
     struct block   *bp;
     u_char    *paddr;
{
     register struct block    *bp1;
     register struct etherpup	   *ep;

     if ((bp1 = allocb(sizeof(struct etherpup))) == 0) {
	  printf("ARP: can't alloc block for resolve\n");
bad:
	  while (bp != 0) {
	       bp1 = bp->next;
	       freeb(bp);
	       bp = bp1;
	  }
	  return 0;
     }

     ep = (struct etherpup *)bp1->wptr;
     bp1->wptr += sizeof(struct etherpup);
     if (arp_getaddr(i, paddr, ep->dhost) < 0) {
	  freeb(bp1);
	  goto bad;
     }

     ep->type = hfirst_short(Arp_proto[i].type);
     bp1->next = bp;
     return bp1;
}

arp_disable(i)
     u_short   i;
{
     register struct proto    *pr = &Arp_proto[i];
     register struct arp      *arp;

     if (i >= NARPROTO || pr->psize == 0) {
          printf("ARP: disable on un-enabled channel %d\n", i);
	  return;
     }

     debug(printf("ARP: disabled channel %d\n", i));

     pr->psize = 0;
}

arp_broadcast(hdaddr)
     u_char    *hdaddr;
{
     bcopy(broadaddr, hdaddr, hd_size);
}


static install(pr, paddr, hdaddr)
     register struct proto    *pr;
     u_char    *paddr, *hdaddr;
{
     register struct arp      *arp, *empty;

     debug(printf("ARP: install()\n"));

          /* Look for an arp pair for this address, or a free one, or LRU */
     for (empty = arp = pr->pair; arp < &pr->pair[NPAIR]; ++arp) {
          if (bcmp(arp->paddr, paddr, pr->psize))
	       break;
          if (bcmp(arp->paddr, pzero, pr->psize))
	       (empty = arp)->time = arp_clock + 1;
	  else if (arp_clock - arp->time > arp_clock - empty->time)
	       empty = arp;
     }

     if (arp == &pr->pair[NPAIR])
	  (arp = empty)->time = ++arp_clock;

     bcopy(paddr, arp->paddr, pr->psize);
     bcopy(hdaddr, arp->hdaddr, hd_size);

     debug(printf("ARP: installed paddr"));
     debug(print_addr(paddr, pr->psize));
     debug(printf(" at ethernet"));
     debug(print_addr(hdaddr, hd_size));
     debug(printf(" on channel %d\n", pr - Arp_proto));

     return 0;
}

#define OUT_SIZE    (sizeof(struct etherpup) + sizeof(struct ether_arp) \
                     - 2*(MAX_HARDSIZE + MAX_PRSIZE) + 2*(hd_size + pr_size))

static request(pr, paddr)
     register struct proto    *pr;
     u_char    *paddr;
{
     register struct queue    *q;
     register struct block    *bp;
     struct ether_arp         *arpkt;
     struct etherpup         *ep;
     long      my_paddr, his_paddr;

     debug(printf("ARP: request()\n"));

     pr_size = pr->psize;
     pr_hfirst = pr->phfirst;

     q = WR(pr->ptr->rdq);
     if ((bp = allocb(OUT_SIZE)) == 0) {
          printf("ARP: can't alloc block for request\n");
          return;
     }
     if (bp->lim - bp->wptr < OUT_SIZE) {
          freeb(bp);
	  printf("ARP: block too small for request\n");
	  return;
     }

     ep = (struct etherpup *)bp->wptr;
     arpkt = (struct ether_arp *)(bp->wptr + sizeof(struct etherpup));
     bp->wptr += OUT_SIZE;

     bcopy(broadaddr, ep->dhost, hd_size);
     ep->type = hfirst_short(ETHERPUP_ARPTYPE);

     arpkt->arp_hrd = hfirst_short(ARPHRD_ETHER);
     arpkt->arp_pro = hfirst_short(pr->type);
     arpkt->arp_hln = hd_size;
     arpkt->arp_pln = pr_size;
     arpkt->arp_op = hfirst_short(ARPOP_REQUEST);

     bcopy(pr->pair[0].hdaddr, hd_sender, hd_size);
     bcopy(pr->pair[0].paddr, pr_sender, pr_size);
     bcopy(paddr, pr_target, pr_size);
     bcopy(hzero, hd_target, hd_size);
     pack(arpkt);

     debug(printf("ARP: sending a request for address"));
     debug(print_addr(paddr, pr->psize));
     debug(printf(" on channel %d\n", pr - Arp_proto));

     if (q->next->flag & QFULL) {
          freeb(bp);
          debug(printf("ARP: QFULL in request()\n"));
     } else {
          (*q->next->qinfo->putp)(q->next, bp);
	  putctl(q->next, M_DELIM);
     }
}


int arp_open(), arp_close(), arp_iput(), arp_srv(), arp_bypass(), arp_rcvpkt();
static struct qinit arp_rinit = {
          arp_iput, arp_srv, arp_open, arp_close, 750, 250
};
static struct qinit arp_winit = {
          arp_bypass, NULL, arp_open, arp_close, 0, 0
};
struct streamtab arpinfo = { &arp_rinit, &arp_winit };


arp_open(q, dev)
     register struct queue    *q;
     dev_t     dev;
{
     register int   i;
     register struct arp_dev  *arpd, *narp;

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

     dev = physical(dev);

     /* Look for a free per-device structure */
     narp = 0;
     for (arpd = Arp_dev; arpd < &Arp_dev[NARP]; ++arpd) {
          if (arpd->pdev == dev) {
	       printf("ARP: multiple arps on device (%d,%d)\n",
	                 major(dev), minor(dev));
               return 0;
          }
	  if (arpd->pdev == 0)
	       narp = arpd;
     }
     if (narp == 0)
          return 0;   /* Open fails: no more line disciplines */

     narp->pdev = dev;
     narp->delim_count = 0;
     bcopy(hzero, narp->hdaddr, MAX_HARDSIZE);
     narp->rdq = q;

     q->flag |= QDELIM|QNOENB;
     q->ptr = (caddr_t)narp;
     return 1;
}

arp_close(q)
     register struct queue    *q;
{
     register struct arp_dev  *arpd;
     int       n, i;

     arpd = (struct arp_dev *)q->ptr;
     arpd->pdev = 0;
     q->ptr = 0;
     n = (arpd - Arp_dev) * NPROTO;
     for (i = n; i < n + NPROTO; ++i)
          Arp_proto[i].psize = 0;
}

arp_bypass(q, bp)
     register struct queue    *q;
     register struct block    *bp;
{
     (*q->next->qinfo->putp)(q->next, bp);
}

arp_misc(q, bp)
     struct queue    *q;
     struct block    *bp;
{
     register union stmsg *sp;

     if (bp->type == M_IOCACK) {
	  sp = (union stmsg *)bp->rptr;
	  if (sp->iocx.com == ENIOADDR)
	       bcopy(sp->iocx.xxx, ((struct arp_dev *)q->ptr)->hdaddr,
			 MAX_HARDSIZE);
     }
     /* Anything else, just pass on to the next guy */
     (*q->next->qinfo->putp)(q->next, bp);
}

arp_iput(q, bp)
     struct queue    *q;
     struct block    *bp;
{
     packet_putp(q, bp, &((struct arp_dev *)q->ptr)->delim_count, 0, arp_misc);
}

arp_false(q)
     struct queue   *q;
{
     return 0;
}

arp_srv(q)
     struct queue   *q;
{
     packet_srvp(q, &((struct arp_dev *)q->ptr)->delim_count,
		    arp_false, arp_false, arp_rcvpkt, 0);
}

arp_rcvpkt(q, bp, partial)
     register struct queue    *q;
     register struct block    *bp;
{
     register struct ether_arp *arpkt;
     register struct proto    *pr;
     register struct etherpup *ep;
     register int        i, type;
     short     op;
     int       ps;

     if (partial || bp == NULL) {
	  free_blocks(bp);
	  return;
     }
     i = sizeof(struct etherpup) + sizeof(struct ether_arp);
     if ((bp = packet_pullup(bp, i)) == 0) {
	  printf("ARP: bad packet\n");
	  return;
     }
     if (bp->next)
	  free_blocks(bp);

     pr = &Arp_proto[((struct arp_dev *)q->ptr - Arp_dev) * NPROTO];
     arpkt = (struct ether_arp *)(bp->rptr + sizeof(struct etherpup));
     type = hfirst_short(arpkt->arp_pro);
     op = hfirst_short(arpkt->arp_op);

     debug(printf("ARP: rcvd arp %s for protocol 0x%x\n",
                    (op == ARPOP_REQUEST ? "request" :
	                 (op == ARPOP_REPLY ? "reply" : "(BAD)")), type));

     ps = spl6();

     for (i = 0; i < NPROTO; ++i, ++pr) {
          if (pr->psize != 0 && pr->type == type)
	       break;
     }
     if (i == NPROTO)
          goto out; /* Not one of the types we recognize */

     pr_size = pr->psize;
     pr_hfirst = pr->phfirst;
     unpack(arpkt);

     if (!bcmp(pr_target, pr->pair[0].paddr, pr_size)) {
out:
          splx(ps);
          freeb(bp);
	  return;
     }
     if (bcmp(pr_sender, pr->pair[0].paddr, pr_size)) {
          printf("ARP: someone is pretending to be me!!!\n");
          goto out;
     }
     if (op == ARPOP_REPLY && !bcmp(hd_target, pr->pair[0].hdaddr, hd_size))
          goto out;

     install(pr, pr_sender, hd_sender);
     if (op != ARPOP_REQUEST)
          goto out;

     arpkt->arp_hrd = hfirst_short(ARPHRD_ETHER);
     arpkt->arp_op = hfirst_short(ARPOP_REPLY);

     debug(printf("ARP: sending reply to"));
     debug(print_addr(pr_sender, pr_size));
     debug(printf(" at ethernet"));
     debug(print_addr(hd_sender, hd_size));
     debug(printf(" on channel %d\n", pr - Arp_proto));

     bcopy(pr_sender, pr_target, pr_size);
     bcopy(hd_sender, hd_target, hd_size);
     bcopy(pr->pair[0].paddr, pr_sender, pr_size);
     bcopy(pr->pair[0].hdaddr, hd_sender, hd_size);
     pack(arpkt);

     ep = (struct etherpup *)(bp->rptr += sizeof(struct etherpup)
                                             - sizeof(struct etherpup));
     bcopy(hd_target, ep->dhost, hd_size);
     ep->type = hfirst_short(ETHERPUP_ARPTYPE);

     splx(ps);

     q = WR(q);
     if (q->next->flag & QFULL) {
          freeb(bp);
          debug(printf("ARP: QFULL on reply\n"));
     } else {
          (*q->next->qinfo->putp) (q->next, bp);
          putctl(q->next, M_DELIM);
     }
}


static pack(arpkt)
     struct ether_arp    *arpkt;
{
     register u_char     *p = arpkt->arp_addr;
     long      paddr;

     debug(printf("ARP: packing arp from"));
     debug(print_addr(pr_sender, pr_size));
     debug(printf(" at"));
     debug(print_addr(hd_sender, hd_size));
     debug(printf(", target is"));
     debug(print_addr(pr_target, pr_size));
     debug(printf(" (at"));
     debug(print_addr(hd_target, hd_size));
     debug(printf(")\n"));

     bcopy(hd_sender, p, hd_size);
     paddr = order(*(long *)pr_sender, pr_size, pr_hfirst);
     bcopy(&paddr, p += hd_size, pr_size);
     bcopy(hd_target, p += pr_size, hd_size);
     paddr = order(*(long *)pr_target, pr_size, pr_hfirst);
     bcopy(&paddr, p += hd_size, pr_size);
}

static unpack(arpkt)
     struct ether_arp    *arpkt;
{
     register u_char     *p = arpkt->arp_addr;
     long      paddr;

     bcopy(p, hd_sender, hd_size);
     paddr = order(*(long *)(p += hd_size), pr_size, pr_hfirst);
     bcopy(&paddr, pr_sender, pr_size);
     bcopy(p += pr_size, hd_target, hd_size);
     paddr = order(*(long *)(p += hd_size), pr_size, pr_hfirst);
     bcopy(&paddr, pr_target, pr_size);

     debug(printf("ARP: unpacked arp from"));
     debug(print_addr(pr_sender, pr_size));
     debug(printf(" at"));
     debug(print_addr(hd_sender, hd_size));
     debug(printf(", target is"));
     debug(print_addr(pr_target, pr_size));
     debug(printf(" (at"));
     debug(print_addr(hd_target, hd_size));
     debug(printf(")\n"));
}


static bcmp(a, b, n)
     register u_char     *a, *b;
     register int        n;
{
     while (n-- > 0)
          if(*a++ != *b++)
               return 0;
     return 1;
}


#ifdef DEBUG

print_addr(addr, size)
     register u_char     *addr;
     register u_short    size;
{
     while (size-- > 0)
          printf(" %x", *addr++);
}

#endif DEBUG

#endif NARP