arpd.c 5.82 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.
 */

#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>
#include <bstring.h>

int arpverbose = 0;
extern u_long proxyaddr;

/*
 * 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, u_char* etherdst);
extern unsigned long FindGateway(unsigned long);

struct ether_addr {
   unsigned char  ether_addr_octet[6];
};
extern struct ether_addr *ether_aton(char *s);


/*
 *----------------------------------------------------------------------
 *
 * ArpRequest --
 *
 *	Handle an ARP request.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A response is transmitted for the ARP request.
 *
 *----------------------------------------------------------------------
 */
int
ArpRequest(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;
    u_char eaddr[6];
 
    /*
     * 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((void *) packet.arp.arp_spa, (void *) &senderAddr,
	    sizeof(senderAddr));
    bcopy((void *) packet.arp.arp_tpa, (void *) &targetAddr,
	    sizeof(targetAddr));

    if(arpverbose) {
	printf("ARP request: address 0x%x\n", targetAddr);
    }
#ifdef SIM_PROXY
    if(targetAddr == senderAddr)
	return(0);
#endif

    if(!FindArpReply(targetAddr, eaddr)) 
	return 0;

    if(arpverbose) {
	char *c;
	c = eaddr;
	printf("ARP request: address 0x%x, hardware %x:%x:%x:%x:%x:%x\n", targetAddr,
	       c[0],c[1],c[2],c[3],c[4],c[5]);
    }

    /*
     * 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((void *) &targetAddr, (void *) packet.arp.arp_spa,
	    sizeof(targetAddr));
    bcopy((void *) &senderAddr, (void *) packet.arp.arp_tpa,
	    sizeof(senderAddr));
    bcopy((void *) &packet.arp.arp_sha, (void *) &packet.arp.arp_tha,
	    6);
    bcopy((void *) &eaddr, (void *) &packet.arp.arp_sha,
	    6);
    packet.arp.ea_hdr.ar_op = htons(ARPOP_REPLY);
    bcopy((void *) &packet.hdr.ether_shost, (void *) &packet.hdr.ether_dhost,
	    6);
    bcopy((void *) &packet, pout, ARP_PACKET_SIZE);
    (*poutlen) = ARP_PACKET_SIZE;
    return 1;
}

static int
FindArpReply(unsigned long dstipaddr, u_char *etherdst)
{
    FILE *pfd;
    unsigned int gateway;
    int found;

    gateway = FindGateway(dstipaddr);
    if (gateway == (unsigned int ) -1) {
	return 0;
    }

#ifdef sparc 
    pfd = popen("/etc/arp -a", "r");
#endif
#ifdef sgi
    pfd = popen("/usr/etc/arp -a", "r");
#endif

    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];
	n = fscanf(pfd, "%s (%d.%d.%d.%d) at %s\n", 
		name, &ip1,&ip2,&ip3,&ip4, eaddrstring);
	if (n < 0) 
		break;
	if (n != 6) 
		continue;

#ifdef sparc
	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) )  {
	        u_char *e = ether_aton(eaddrstring);
		if (e != NULL) {
			*etherdst = *e;
			found = 1;
		}
		break;
	}
#endif  /* sparc */
#ifdef sgi
       if ( ((( ip1*256+ip2)*256+ip3)*256+ip4) == ia->s_addr){
	        struct ether_addr *e = ether_aton(eaddrstring);
		if (e != NULL) {
		    bcopy((void *)e, etherdst, 6);
			found = 1;
                        printf("arp -a found: %s\n",eaddrstring);
		}
		break;
        } 
#endif /* sgi */
    } 
    pclose(pfd);
    return found;
}