arpd.c 6.3 KB
/* 
 * arpd.c --
 *
 *	A program to reply to ARP and RARP requests.  This program
 *	only translates between Internet and Ethernet addresses.  See
 *	RFC826 for details of the ARP protocol, and RFC903 for details
 *	of the RARP protocol.
 *
 * Copyright 1989 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /root/leakn64/depot/rf/sw/bbplayer/simos/apps/unix/ethersim/sun/arpd.c,v 1.1.1.1 2002/05/29 01:09:09 blythe Exp $ SPRITE (Berkeley)";
#endif not lint


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/if_ether.h>


int arpverbose = 0;

/*
 * The ARP/RARP packet size is defined below.  Because of structure
 * alignment differences between machines, it isn't safe to use
 * sizeof with the structure.
 */

#define ARP_PACKET_SIZE 42

static int FindArpReply(unsigned long dstipaddr, struct ether_addr *etherdst);
extern unsigned long FindGateway(unsigned long);

/*
 *----------------------------------------------------------------------
 *
 * ArpRequest --
 *
 *	Handle an ARP request.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A response is transmitted for the ARP request.
 *
 *----------------------------------------------------------------------
 */

int
ArpRequest(pin, pinlen, pout, poutlen)
	void *pin;
	int pinlen;
	void *pout;
	int *poutlen;
{
    struct {
	struct ether_header hdr;
	struct ether_arp arp;
    } packet;
    int bytesRead, etherType, protocolType, hardwareType, arpOp;
    unsigned long senderAddr, targetAddr;
    struct ether_addr eaddr;

    /*
     * Read and validate the ARP packet.
     */
    bytesRead = (sizeof(packet) > pinlen) ? pinlen : sizeof(packet);
    bcopy(pin, &packet, bytesRead); 
    if (bytesRead < 0) {
	printf("arpd couldn't read ARP packet: %d\n", errno);
	return 0;
    }
    if (bytesRead < ARP_PACKET_SIZE) {
	if (arpverbose)
		printf("arpd got short ARP packet: only %d bytes\n", bytesRead);
	return 0;
    }
    etherType = ntohs(packet.hdr.ether_type);
    if (etherType != ETHERTYPE_ARP) {
	if (arpverbose)
		printf("arpd got ARP packet with ether_type 0x%x\n", etherType);
	return 0;
    }
    arpOp = ntohs(packet.arp.ea_hdr.ar_op);
    if (arpOp != ARPOP_REQUEST) {
	if (arpverbose) {
	    printf("arpd got ARP packet with unknown op %d\n", arpOp);
	}
	return 0;
    }
    hardwareType = ntohs(packet.arp.ea_hdr.ar_hrd);
    if (hardwareType != ARPHRD_ETHER) {
	if (arpverbose) {
	    printf("arpd got ARP packet with unknown hardware type 0x%x\n",
		    hardwareType);
	}
	return 0;
    }
    protocolType = ntohs(packet.arp.ea_hdr.ar_pro);
    if (protocolType != ETHERTYPE_IP) {
	if (arpverbose) {
	    printf("arpd got ARP packet with unknown protocol type 0x%x\n",
		    protocolType);
	}
	return 0;
    }


    bcopy((char *) packet.arp.arp_spa, (char *) &senderAddr,
	    sizeof(senderAddr));
    bcopy((char *) packet.arp.arp_tpa, (char *) &targetAddr,
	    sizeof(targetAddr));

  
    
    if(!FindArpReply(targetAddr, &eaddr)) {
       /* 
        * If we do not find the address in the arp cache,
        * do a ping of the address using a "system" call
        * This way we will find it the next time, maybe
        */
       char buf[128];
       struct in_addr *in =  (struct in_addr *)&targetAddr;
       strcpy(buf, "/usr/etc/ping ");
       strcat(buf, inet_ntoa(*in));
       strcat(buf, " &");
       if (arpverbose)
          printf("%#x not in arp cache, doing %s\n", targetAddr, buf);
       system(buf);
	return 0;
    }
    /*
     * Reverse sender and target fields, and respond with the appropriate
     * Ethernet address.  No need to fill in the source in the packet
     * header:  the kernel automatically overwrites it.
     */

    bcopy((char *) &targetAddr, (char *) packet.arp.arp_spa,
	    sizeof(targetAddr));
    bcopy((char *) &senderAddr, (char *) packet.arp.arp_tpa,
	    sizeof(senderAddr));
    bcopy((char *) &packet.arp.arp_sha, (char *) &packet.arp.arp_tha,
	    sizeof(struct ether_addr));
    bcopy((char *) &eaddr, (char *) &packet.arp.arp_sha,
	    sizeof(struct ether_addr));
    packet.arp.ea_hdr.ar_op = htons(ARPOP_REPLY);
    bcopy((char *) &packet.hdr.ether_shost, (char *) &packet.hdr.ether_dhost,
	    sizeof(struct ether_addr));
    bcopy((char *) &packet, pout, ARP_PACKET_SIZE);
    (*poutlen) = ARP_PACKET_SIZE;
    return 1;
}

static int
FindArpReply(dstipaddr, etherdst)
	unsigned long dstipaddr;
	struct ether_addr *etherdst;
{
    FILE *pfd;
    unsigned int gateway;
    int found;

    gateway = FindGateway(dstipaddr);
    if (gateway == (unsigned int ) -1) {
	return 0;
    }
    pfd = popen("/usr/etc/arp -a", "r");
    if (pfd == (FILE *) NULL) {
	return 0;
    }
    found = 0;
    while(1) {
	unsigned int ip1,ip2,ip3,ip4,e1,e2,e3,e4,e5,e6;
	struct in_addr *ia = (struct in_addr *) &gateway;
	int n;
    	char name[256], eaddrstring[32];
	char line[1024];
	extern int verbose;

	if (fgets(line, sizeof(line), pfd) == 0)
	  break;
	if (verbose) {
	  fprintf(stderr, "arp request result: %s", line);
	}

	n = sscanf(line, "%s (%d.%d.%d.%d) at %s\n", 
		name, &ip1,&ip2,&ip3,&ip4, eaddrstring);
	if (verbose) {
	  fprintf(stderr, "n=%d name=%s\n", n, name);
	}
	if (n < 0) 
		break;
	if (n != 6) 
		continue;
	if (    (ip1 == ia->S_un.S_un_b.s_b1) &&
		(ip2 == ia->S_un.S_un_b.s_b2) &&
		(ip3 == ia->S_un.S_un_b.s_b3) &&
		(ip4 == ia->S_un.S_un_b.s_b4))  {
	        struct ether_addr *e = ether_aton(eaddrstring);
                if (verbose) {
#define	A(x) (e->ether_addr_octet[x])
                   fprintf(stderr, "ARP entry found %s %d.%d.%d.%d %s %x:%x:%x:%x:%x:%x\n", 
                           name, ip1, ip2, ip3, ip4, eaddrstring, A(1), A(2), A(3), A(4), A(5), A(6));
                }
		if (e != NULL) {
			*etherdst = *e;
			found = 1;
		}
		break;
	}
    } 
    pclose(pfd);
    return found;
}