rarpd.c revision 8857
16823Swpaul/*
26823Swpaul * Copyright (c) 1990 The Regents of the University of California.
36823Swpaul * All rights reserved.
46823Swpaul *
56823Swpaul * Redistribution and use in source and binary forms, with or without
66823Swpaul * modification, are permitted provided that: (1) source code distributions
76823Swpaul * retain the above copyright notice and this paragraph in its entirety, (2)
86823Swpaul * distributions including binary code include the above copyright notice and
96823Swpaul * this paragraph in its entirety in the documentation or other materials
106823Swpaul * provided with the distribution, and (3) all advertising materials mentioning
116823Swpaul * features or use of this software display the following acknowledgement:
126823Swpaul * ``This product includes software developed by the University of California,
136823Swpaul * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
146823Swpaul * the University nor the names of its contributors may be used to endorse
156823Swpaul * or promote products derived from this software without specific prior
166823Swpaul * written permission.
176823Swpaul * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
186823Swpaul * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
196823Swpaul * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
206823Swpaul */
216823Swpaul#ifndef lint
226823Swpaulchar copyright[] =
236823Swpaul"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
246823Swpaul All rights reserved.\n";
256823Swpaul#endif /* not lint */
266823Swpaul
276823Swpaul#ifndef lint
286823Swpaulstatic char rcsid[] =
298857Srgrimes    "@(#) $Header: /home/ncvs/src/usr.sbin/rarpd/rarpd.c,v 1.3 1995/04/02 01:35:54 wpaul Exp $ (LBL)";
306823Swpaul#endif
316823Swpaul
326823Swpaul
338857Srgrimes/*
346823Swpaul * rarpd - Reverse ARP Daemon
356823Swpaul *
366823Swpaul * Usage:	rarpd -a [ -f ] [ hostname ]
376823Swpaul *		rarpd [ -f ] interface [ hostname ]
388857Srgrimes *
396823Swpaul * 'hostname' is optional solely for backwards compatibility with Sun's rarpd.
406823Swpaul * Currently, the argument is ignored.
416823Swpaul */
426823Swpaul
436823Swpaul#include <stdio.h>
446823Swpaul#include <syslog.h>
456823Swpaul#include <string.h>
466823Swpaul#include <strings.h>
476823Swpaul#include <sys/types.h>
486823Swpaul/* SunOS 4.x defines this while 3.x does not. */
496823Swpaul#ifdef __sys_types_h
506823Swpaul#define SUNOS4
516823Swpaul#endif
526823Swpaul#include <sys/time.h>
536823Swpaul#include <net/bpf.h>
546823Swpaul#include <sys/socket.h>
556823Swpaul#include <sys/ioctl.h>
566823Swpaul#include <net/if.h>
576823Swpaul#include <netinet/in.h>
586823Swpaul#include <netinet/if_ether.h>
596823Swpaul#include <sys/errno.h>
606823Swpaul#include <sys/file.h>
616823Swpaul#include <netdb.h>
626823Swpaul
636823Swpaul#ifdef SUNOS4
646823Swpaul#include <dirent.h>
656823Swpaul#else
666823Swpaul#include <sys/dir.h>
676823Swpaul#endif
686823Swpaul
696823Swpaul/*
706823Swpaul * Map field names in ether_arp struct.  What a pain in the neck.
716823Swpaul */
726823Swpaul#if !defined(SUNOS4) && !defined(__FreeBSD__)
736823Swpaul#undef arp_sha
746823Swpaul#undef arp_spa
756823Swpaul#undef arp_tha
766823Swpaul#undef arp_tpa
776823Swpaul#define arp_sha arp_xsha
786823Swpaul#define arp_spa arp_xspa
796823Swpaul#define arp_tha arp_xtha
806823Swpaul#define arp_tpa arp_xtpa
816823Swpaul#endif
826823Swpaul
836823Swpaul#ifndef __GNUC__
846823Swpaul#define inline
856823Swpaul#endif
866823Swpaul
876823Swpaulextern int errno;
887577Swpaulextern int ether_ntohost __P((char *, struct ether_addr *));
896823Swpaul
906823Swpaul/*
918857Srgrimes * The structure for each interface.
926823Swpaul */
936823Swpaulstruct if_info {
946823Swpaul	int 	ii_fd;		/* BPF file descriptor */
956823Swpaul	u_char	ii_eaddr[6];	/* Ethernet address of this interface */
966823Swpaul	u_long	ii_ipaddr;	/* IP address of this interface */
976823Swpaul	u_long	ii_netmask;	/* subnet or net mask */
986823Swpaul	struct if_info *ii_next;
996823Swpaul};
1006823Swpaul
1016823Swpaul/*
1026823Swpaul * The list of all interfaces that are being listened to.  rarp_loop()
1036823Swpaul * "selects" on the descriptors in this list.
1046823Swpaul */
1056823Swpaulstruct if_info *iflist;
1066823Swpaul
1076823Swpaulextern char *malloc();
1086823Swpaulextern void exit();
1096823Swpaul
1106823Swpaulu_long ipaddrtonetmask();
1116823Swpaulvoid init_one();
1126823Swpaulvoid init_all();
1136823Swpaulvoid rarp_loop();
1146823Swpaulvoid lookup_eaddr();
1156823Swpaulvoid lookup_ipaddr();
1166823Swpaul
1176823Swpaulvoid
1186823Swpaulmain(argc, argv)
1196823Swpaul	int argc;
1206823Swpaul	char **argv;
1216823Swpaul{
1226823Swpaul	int op, pid;
1236823Swpaul	char *ifname, *hostname, *name;
1246823Swpaul
1256823Swpaul	int aflag = 0;		/* listen on "all" interfaces  */
1266823Swpaul	int fflag = 0;		/* don't fork */
1276823Swpaul
1286823Swpaul	extern char *optarg;
1296823Swpaul	extern int optind, opterr;
1306823Swpaul
1316823Swpaul	if (name = strrchr(argv[0], '/'))
1326823Swpaul		++name;
1336823Swpaul	else
1346823Swpaul		name = argv[0];
1356823Swpaul	if (*name == '-')
1366823Swpaul		++name;
1376823Swpaul
1388857Srgrimes	/*
1396823Swpaul	 * All error reporting is done through syslogs.
1406823Swpaul	 */
1416823Swpaul	openlog(name, LOG_PID, LOG_DAEMON);
1426823Swpaul
1436823Swpaul	opterr = 0;
1446823Swpaul	while ((op = getopt(argc, argv, "af")) != EOF) {
1456823Swpaul		switch (op) {
1466823Swpaul		case 'a':
1476823Swpaul			++aflag;
1486823Swpaul			break;
1496823Swpaul
1506823Swpaul		case 'f':
1516823Swpaul			++fflag;
1526823Swpaul			break;
1536823Swpaul
1546823Swpaul		default:
1556823Swpaul			usage();
1566823Swpaul			/* NOTREACHED */
1576823Swpaul		}
1586823Swpaul	}
1596823Swpaul	ifname = argv[optind++];
1606823Swpaul	hostname =  ifname ? argv[optind] : 0;
1616823Swpaul	if ((aflag && ifname) || (!aflag && ifname == 0))
1626823Swpaul		usage();
1636823Swpaul
1646823Swpaul	if (aflag)
1656823Swpaul		init_all();
1666823Swpaul	else
1676823Swpaul		init_one(ifname);
1688857Srgrimes
1696823Swpaul	if (!fflag) {
1706823Swpaul		pid = fork();
1716823Swpaul		if (pid > 0)
1726823Swpaul			/* Parent exits, leaving child in background. */
1736823Swpaul			exit(0);
1746823Swpaul		else if (pid == -1) {
1756823Swpaul			syslog(LOG_ERR, "cannot fork");
1766823Swpaul			exit(1);
1776823Swpaul		}
1786823Swpaul	}
1796823Swpaul	rarp_loop();
1806823Swpaul}
1816823Swpaul
1826823Swpaul/*
1836823Swpaul * Add 'ifname' to the interface list.  Lookup its IP address and network
1846823Swpaul * mask and Ethernet address, and open a BPF file for it.
1856823Swpaul */
1866823Swpaulvoid
1876823Swpaulinit_one(ifname)
1886823Swpaul	char *ifname;
1896823Swpaul{
1906823Swpaul	struct if_info *p;
1916823Swpaul
1926823Swpaul
1936823Swpaul	p = (struct if_info *)malloc(sizeof(*p));
1946823Swpaul	p->ii_next = iflist;
1956823Swpaul	iflist = p;
1966823Swpaul
1976823Swpaul	p->ii_fd = rarp_open(ifname);
1986823Swpaul	lookup_eaddr(p->ii_fd, p->ii_eaddr);
1996823Swpaul	lookup_ipaddr(ifname, &p->ii_ipaddr, &p->ii_netmask);
2006823Swpaul}
2016823Swpaul
2026823Swpaul/*
2036823Swpaul * Initialize all "candidate" interfaces that are in the system
2046823Swpaul * configuration list.  A "candidate" is up, not loopback and not
2056823Swpaul * point to point.
2066823Swpaul */
2076823Swpaulvoid
2086823Swpaulinit_all()
2096823Swpaul{
2106823Swpaul	int fd;
2116823Swpaul	int ifflags;
2126823Swpaul	struct ifreq ibuf[8], tmp_ibuf, *ifptr, *n;
2136823Swpaul	struct ifconf ifc;
2146823Swpaul
2156823Swpaul	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
2166823Swpaul		syslog(LOG_ERR, "socket: %m");
2176823Swpaul		exit(1);
2186823Swpaul	}
2196823Swpaul	ifc.ifc_len = sizeof ibuf;
2206823Swpaul	ifc.ifc_buf = (caddr_t)ibuf;
2216823Swpaul	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
2226823Swpaul	    ifc.ifc_len < sizeof(struct ifreq)) {
2236823Swpaul		syslog(LOG_ERR, "SIOCGIFCONF: %m");
2246823Swpaul		exit(1);
2256823Swpaul	}
2266823Swpaul	ifptr = ifc.ifc_req;
2276823Swpaul	ifflags = ifptr->ifr_flags;
2286823Swpaul	n = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
2296823Swpaul	while (ifptr < n) {
2306823Swpaul		bcopy((char *)ifptr, (char *)&tmp_ibuf, sizeof(struct ifreq));
2316823Swpaul		if (ioctl(fd, SIOCGIFFLAGS, (char *)&tmp_ibuf) < 0) {
2326823Swpaul			syslog(LOG_ERR, "SIOCGIFFLAGS: %m");
2336823Swpaul			exit(1);
2346823Swpaul		}
2356823Swpaul		if (ifptr->ifr_flags == ifflags && (tmp_ibuf.ifr_flags &
2366823Swpaul			(IFF_UP | IFF_LOOPBACK | IFF_POINTOPOINT)) == IFF_UP)
2376823Swpaul			init_one(ifptr->ifr_name);
2386823Swpaul		if(ifptr->ifr_addr.sa_len)	/* Dohw! */
2396823Swpaul			ifptr = (struct ifreq *) ((caddr_t) ifptr +
2406823Swpaul			ifptr->ifr_addr.sa_len -
2416823Swpaul			sizeof(struct sockaddr));
2426823Swpaul		ifptr++;
2436823Swpaul	}
2446823Swpaul	(void)close(fd);
2456823Swpaul}
2466823Swpaul
2476823Swpaulusage()
2486823Swpaul{
2496823Swpaul	(void)fprintf(stderr, "usage: rarpd [ -af ] [ interface ]\n");
2506823Swpaul	exit(1);
2516823Swpaul}
2526823Swpaul
2536823Swpaulstatic int
2546823Swpaulbpf_open()
2556823Swpaul{
2566823Swpaul	int fd;
2576823Swpaul	int n = 0;
2586823Swpaul	char device[sizeof "/dev/bpf000"];
2596823Swpaul
2606823Swpaul	/*
2618857Srgrimes	 * Go through all the minors and find one that isn't in use.
2626823Swpaul	 */
2636823Swpaul	do {
2646823Swpaul		(void)sprintf(device, "/dev/bpf%d", n++);
2656823Swpaul		fd = open(device, O_RDWR);
2666823Swpaul	} while (fd < 0 && errno == EBUSY);
2676823Swpaul
2686823Swpaul	if (fd < 0) {
2696823Swpaul		syslog(LOG_ERR, "%s: %m", device);
2706823Swpaul		exit(-1);
2716823Swpaul	}
2726823Swpaul	return fd;
2736823Swpaul}
2746823Swpaul
2756823Swpaul/*
2766823Swpaul * Open a BPF file and attach it to the interface named 'device'.
2776823Swpaul * Set immediate mode, and set a filter that accepts only RARP requests.
2786823Swpaul */
2796823Swpaulint
2806823Swpaulrarp_open(device)
2816823Swpaul	char *device;
2826823Swpaul{
2836823Swpaul	int fd;
2846823Swpaul	struct ifreq ifr;
2856823Swpaul	int immediate, link_type;
2866823Swpaul
2876823Swpaul	static struct bpf_insn insns[] = {
2886823Swpaul                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
2896823Swpaul                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_REVARP, 0, 3),
2906823Swpaul                BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20),
2916823Swpaul                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REVREQUEST, 0, 1),
2926823Swpaul                BPF_STMT(BPF_RET+BPF_K, sizeof(struct ether_arp) +
2936823Swpaul                                sizeof(struct ether_header)),
2946823Swpaul                BPF_STMT(BPF_RET+BPF_K, 0),
2956823Swpaul        };
2966823Swpaul
2978857Srgrimes        static struct bpf_program filter = {
2988857Srgrimes                sizeof insns / sizeof(insns[0]),
2996823Swpaul                (struct bpf_insn *)&insns
3006823Swpaul        };
3016823Swpaul
3026823Swpaul	fd = bpf_open();
3038857Srgrimes	/*
3046823Swpaul	 * Set immediate mode so packets are processed as they arrive.
3056823Swpaul	 */
3066823Swpaul	immediate = 1;
3076823Swpaul	if (ioctl(fd, BIOCIMMEDIATE, &immediate) < 0) {
3086823Swpaul		syslog(LOG_ERR, "BIOCIMMEDIATE: %m");
3096823Swpaul		exit(1);
3106823Swpaul	}
3116823Swpaul	(void)strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
3126823Swpaul	if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
3136823Swpaul		syslog(LOG_ERR, "BIOCSETIF: %m");
3146823Swpaul		exit(1);
3156823Swpaul	}
3166823Swpaul	/*
3176823Swpaul	 * Check that the data link layer is an Ethernet; this code won't
3186823Swpaul	 * work with anything else.
3196823Swpaul	 */
3206823Swpaul	if (ioctl(fd, BIOCGDLT, &link_type) < 0) {
3216823Swpaul		syslog(LOG_ERR, "BIOCGDLP: %m");
3226823Swpaul		exit(1);
3236823Swpaul	}
3246823Swpaul	if (link_type != DLT_EN10MB) {
3256823Swpaul		syslog(LOG_ERR, "%s not on ethernet", device);
3266823Swpaul		exit(1);
3276823Swpaul	}
3286823Swpaul	/*
3296823Swpaul	 * Set filter program.
3306823Swpaul	 */
3316823Swpaul	if (ioctl(fd, BIOCSETF, (caddr_t)&filter) < 0) {
3326823Swpaul		syslog(LOG_ERR, "BIOCSETF: %m");
3336823Swpaul		exit(1);
3346823Swpaul	}
3356823Swpaul	return fd;
3366823Swpaul}
3376823Swpaul
3386823Swpaul/*
3396823Swpaul * Perform various sanity checks on the RARP request packet.  Return
3406823Swpaul * false on failure and log the reason.
3416823Swpaul */
3426823Swpaulstatic int
3436823Swpaulrarp_check(p, len)
3446823Swpaul	u_char *p;
3456823Swpaul	int len;
3466823Swpaul{
3476823Swpaul	struct ether_header *ep = (struct ether_header *)p;
3486823Swpaul	struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep));
3496823Swpaul
3506823Swpaul	if (len < sizeof(*ep) + sizeof(*ap)) {
3516823Swpaul		syslog(LOG_ERR, "truncated request");
3526823Swpaul		return 0;
3536823Swpaul	}
3546823Swpaul	/*
3556823Swpaul	 * XXX This test might be better off broken out...
3566823Swpaul	 */
3576823Swpaul	if (ep->ether_type != htons(ETHERTYPE_REVARP) ||
3586823Swpaul	    ap->arp_hrd != htons(ARPHRD_ETHER) ||
3596823Swpaul	    ap->arp_op != htons(ARPOP_REVREQUEST) ||
3606823Swpaul	    ap->arp_pro != htons(ETHERTYPE_IP) ||
3616823Swpaul	    ap->arp_hln != 6 || ap->arp_pln != 4) {
3626823Swpaul		syslog(LOG_DEBUG, "request fails sanity check");
3636823Swpaul		return 0;
3646823Swpaul	}
3656823Swpaul	if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
3666823Swpaul		syslog(LOG_DEBUG, "ether/arp sender address mismatch");
3676823Swpaul		return 0;
3686823Swpaul	}
3696823Swpaul	if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
3706823Swpaul		syslog(LOG_DEBUG, "ether/arp target address mismatch");
3716823Swpaul		return 0;
3726823Swpaul	}
3736823Swpaul	return 1;
3746823Swpaul}
3756823Swpaul
3766823Swpaul#ifndef FD_SETSIZE
3776823Swpaul#define FD_SET(n, fdp) ((fdp)->fds_bits[0] |= (1 << (n)))
3786823Swpaul#define FD_ISSET(n, fdp) ((fdp)->fds_bits[0] & (1 << (n)))
3796823Swpaul#define FD_ZERO(fdp) ((fdp)->fds_bits[0] = 0)
3806823Swpaul#endif
3816823Swpaul
3826823Swpaul/*
3838857Srgrimes * Loop indefinitely listening for RARP requests on the
3846823Swpaul * interfaces in 'iflist'.
3856823Swpaul */
3866823Swpaulvoid
3876823Swpaulrarp_loop()
3886823Swpaul{
3896823Swpaul	struct bpf_hdr *bhp;
3906823Swpaul	u_char *pkt;
3916823Swpaul	int cc, fd;
3926823Swpaul	fd_set fds, listeners;
3936823Swpaul	int bufsize, maxfd = 0;
3946823Swpaul	struct if_info *ii;
3956823Swpaul
3966823Swpaul	if (iflist == 0) {
3976823Swpaul		syslog(LOG_ERR, "no interfaces");
3986823Swpaul		exit(1);
3996823Swpaul	}
4006823Swpaul	if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) < 0) {
4016823Swpaul		syslog(LOG_ERR, "BIOCGBLEN: %m");
4026823Swpaul		exit(1);
4036823Swpaul	}
4046823Swpaul	bhp = (struct bpf_hdr *)malloc((unsigned)bufsize);
4056823Swpaul
4066823Swpaul	/*
4076823Swpaul	 * Find the highest numbered file descriptor for select().
4086823Swpaul	 * Initialize the set of descriptors to listen to.
4096823Swpaul	 */
4106823Swpaul	FD_ZERO(&fds);
4116823Swpaul	for (ii = iflist; ii; ii = ii->ii_next) {
4126823Swpaul		FD_SET(ii->ii_fd, &fds);
4136823Swpaul		if (ii->ii_fd > maxfd)
4146823Swpaul			maxfd = ii->ii_fd;
4156823Swpaul	}
4166823Swpaul	while (1) {
4176823Swpaul		listeners = fds;
4188857Srgrimes		if (select(maxfd + 1, &listeners, (struct fd_set *)0,
4196823Swpaul			   (struct fd_set *)0, (struct timeval *)0) < 0) {
4206823Swpaul			syslog(LOG_ERR, "select: %m");
4216823Swpaul			exit(1);
4226823Swpaul		}
4236823Swpaul		for (ii = iflist; ii; ii = ii->ii_next) {
4246823Swpaul			fd = ii->ii_fd;
4256823Swpaul			if (FD_ISSET(fd, &listeners)) {
4266823Swpaul			again:
4276823Swpaul				cc = read(fd, (char *)bhp, bufsize);
4286823Swpaul				/*
4298857Srgrimes				 * Due to a SunOS bug, after 2^31 bytes, the
4308857Srgrimes				 * file offset overflows and read fails with
4316823Swpaul				 * EINVAL.  The lseek() to 0 will fix things.
4326823Swpaul				 */
4336823Swpaul				if (cc < 0) {
4346823Swpaul					if (errno == EINVAL &&
4356823Swpaul					    (long)(lseek(fd, 0L, SEEK_CUR) + bufsize) < 0) {
4366823Swpaul						(void)lseek(fd, 0, 0);
4376823Swpaul						goto again;
4386823Swpaul					}
4396823Swpaul					syslog(LOG_ERR, "read: %m");
4406823Swpaul					exit(1);
4416823Swpaul				}
4426823Swpaul				pkt = (u_char *)bhp + bhp->bh_hdrlen;
4436823Swpaul
4446823Swpaul				if (rarp_check(pkt, (int)bhp->bh_datalen))
4456823Swpaul					rarp_process(ii, pkt);
4466823Swpaul			}
4476823Swpaul		}
4486823Swpaul	}
4496823Swpaul}
4506823Swpaul
4516823Swpaul#ifndef TFTP_DIR
4526823Swpaul#define TFTP_DIR "/tftpboot"
4536823Swpaul#endif
4546823Swpaul
4556823Swpaul/*
4566823Swpaul * True if this server can boot the host whose IP address is 'addr'.
4576823Swpaul * This check is made by looking in the tftp directory for the
4586823Swpaul * configuration file.
4596823Swpaul */
4606823Swpaulrarp_bootable(addr)
4616823Swpaul	u_long addr;
4626823Swpaul{
4636823Swpaul
4646823Swpaul#ifdef SUNOS4
4656823Swpaul	register struct dirent *dent;
4666823Swpaul#else
4676823Swpaul	register struct direct *dent;
4686823Swpaul#endif
4696823Swpaul	register DIR *d;
4706823Swpaul	char ipname[9];
4716823Swpaul	static DIR *dd = 0;
4726823Swpaul
4736823Swpaul	/*
4746823Swpaul	 * XXX   Need to htonl() the IP address or it'll
4756823Swpaul	 * come out backwards.
4766823Swpaul	 */
4776823Swpaul	(void)sprintf(ipname, "%08X", htonl(addr));
4786823Swpaul	/*
4796823Swpaul	 * If directory is already open, rewind it.  Otherwise, open it.
4806823Swpaul	 */
4816823Swpaul	if (d = dd)
4826823Swpaul		rewinddir(d);
4836823Swpaul	else {
4846823Swpaul		if (chdir(TFTP_DIR) == -1) {
4856823Swpaul			syslog(LOG_ERR, "chdir: %m");
4866823Swpaul			exit(1);
4876823Swpaul		}
4886823Swpaul		d = opendir(".");
4896823Swpaul		if (d == 0) {
4906823Swpaul			syslog(LOG_ERR, "opendir: %m");
4916823Swpaul			exit(1);
4926823Swpaul		}
4936823Swpaul		dd = d;
4946823Swpaul	}
4956823Swpaul	while (dent = readdir(d))
4966823Swpaul		if (strncmp(dent->d_name, ipname, 8) == 0)
4976823Swpaul			return 1;
4986823Swpaul	return 0;
4996823Swpaul
5006823Swpaul}
5016823Swpaul
5026823Swpaul/*
5036823Swpaul * Given a list of IP addresses, 'alist', return the first address that
5046823Swpaul * is on network 'net'; 'netmask' is a mask indicating the network portion
5056823Swpaul * of the address.
5066823Swpaul */
5076823Swpaulu_long
5086823Swpaulchoose_ipaddr(alist, net, netmask)
5096823Swpaul	u_long **alist;
5106823Swpaul	u_long net;
5116823Swpaul	u_long netmask;
5126823Swpaul{
5136823Swpaul	for (; *alist; ++alist) {
5146823Swpaul		if ((**alist & netmask) == net)
5156823Swpaul			return **alist;
5166823Swpaul	}
5176823Swpaul	return 0;
5186823Swpaul}
5196823Swpaul
5206823Swpaul/*
5216823Swpaul * A one entry ip/ethernet address cache.
5226823Swpaul */
5236823Swpaulstatic u_long cache_ipaddr;
5246823Swpaulstatic u_char cache_eaddr[6];
5256823Swpaul
5266823Swpaul/*
5276823Swpaul * Answer the RARP request in 'pkt', on the interface 'ii'.  'pkt' has
5286823Swpaul * already been checked for validity.  The reply is overlaid on the request.
5296823Swpaul */
5306823Swpaulrarp_process(ii, pkt)
5316823Swpaul	struct if_info *ii;
5326823Swpaul	u_char *pkt;
5336823Swpaul{
5346823Swpaul	struct ether_header *ep;
5356823Swpaul	struct hostent *hp;
5366823Swpaul	u_long target_ipaddr;
5376823Swpaul	char ename[256];
5386823Swpaul
5396823Swpaul	ep = (struct ether_header *)pkt;
5406823Swpaul	/*
5418857Srgrimes	 * If the address in the one element cache, don't bother
5426823Swpaul	 * looking up names.
5436823Swpaul	 */
5446823Swpaul	if (bcmp((char *)cache_eaddr, (char *)&ep->ether_shost, 6) == 0)
5456823Swpaul		target_ipaddr = cache_ipaddr;
5466823Swpaul	else {
5477577Swpaul		if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0 ||
5486823Swpaul		    (hp = gethostbyname(ename)) == 0)
5496823Swpaul			return;
5506823Swpaul		/*
5516823Swpaul		 * Choose correct address from list.
5526823Swpaul		 */
5536823Swpaul		if (hp->h_addrtype != AF_INET) {
5546823Swpaul			syslog(LOG_ERR, "cannot handle non IP addresses");
5556823Swpaul			exit(1);
5566823Swpaul		}
5578857Srgrimes		target_ipaddr = choose_ipaddr((u_long **)hp->h_addr_list,
5586823Swpaul					      ii->ii_ipaddr & ii->ii_netmask,
5596823Swpaul					      ii->ii_netmask);
5606823Swpaul		if (target_ipaddr == 0) {
5618857Srgrimes			syslog(LOG_ERR, "cannot find %s on %08x",
5626823Swpaul			       ename, ii->ii_ipaddr & ii->ii_netmask);
5636823Swpaul			return;
5646823Swpaul		}
5656823Swpaul		bcopy((char *)&ep->ether_shost, (char *)cache_eaddr, 6);
5666823Swpaul		cache_ipaddr = target_ipaddr;
5676823Swpaul	}
5686823Swpaul	if (rarp_bootable(target_ipaddr))
5696823Swpaul		rarp_reply(ii, ep, target_ipaddr);
5706823Swpaul}
5716823Swpaul
5726823Swpaul/*
5736823Swpaul * Lookup the ethernet address of the interface attached to the BPF
5746823Swpaul * file descriptor 'fd'; return it in 'eaddr'.
5756823Swpaul */
5766823Swpaulvoid
5776823Swpaullookup_eaddr(fd, eaddr)
5786823Swpaul	int fd;
5796823Swpaul	u_char *eaddr;
5806823Swpaul{
5816823Swpaul	struct ifreq ifr;
5826823Swpaul
5836823Swpaul	/* Use BPF descriptor to get ethernet address. */
5846823Swpaul	if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
5856823Swpaul		syslog(LOG_ERR, "SIOCGIFADDR: %m");
5866823Swpaul		exit(1);
5876823Swpaul	}
5886823Swpaul	bcopy((char *)&ifr.ifr_addr.sa_data[0], (char *)eaddr, 6);
5896823Swpaul}
5906823Swpaul
5916823Swpaul/*
5926823Swpaul * Lookup the IP address and network mask of the interface named 'ifname'.
5936823Swpaul */
5946823Swpaulvoid
5956823Swpaullookup_ipaddr(ifname, addrp, netmaskp)
5966823Swpaul	char *ifname;
5976823Swpaul	u_long *addrp;
5986823Swpaul	u_long *netmaskp;
5996823Swpaul{
6006823Swpaul	int fd;
6016823Swpaul	struct ifreq ifr;
6026823Swpaul
6036823Swpaul	/* Use data gram socket to get IP address. */
6046823Swpaul	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
6056823Swpaul		syslog(LOG_ERR, "socket: %m");
6066823Swpaul		exit(1);
6076823Swpaul	}
6086823Swpaul	(void)strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
6096823Swpaul	if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
6106823Swpaul		syslog(LOG_ERR, "SIOCGIFADDR: %m");
6116823Swpaul		exit(1);
6126823Swpaul	}
6136823Swpaul	*addrp = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
6146823Swpaul	if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) {
6156823Swpaul		perror("SIOCGIFNETMASK");
6166823Swpaul		exit(1);
6176823Swpaul	}
6186823Swpaul	*netmaskp = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
6196823Swpaul	/*
6206823Swpaul	 * If SIOCGIFNETMASK didn't work, figure out a mask from
6216823Swpaul	 * the IP address class.
6226823Swpaul	 */
6236823Swpaul	if (*netmaskp == 0)
6246823Swpaul		*netmaskp = ipaddrtonetmask(*addrp);
6256823Swpaul
6266823Swpaul	(void)close(fd);
6276823Swpaul}
6286823Swpaul
6296823Swpaul/*
6306823Swpaul * Poke the kernel arp tables with the ethernet/ip address combinataion
6316823Swpaul * given.  When processing a reply, we must do this so that the booting
6326823Swpaul * host (i.e. the guy running rarpd), won't try to ARP for the hardware
6336823Swpaul * address of the guy being booted (he cannot answer the ARP).
6346823Swpaul */
6356823Swpaulupdate_arptab(ep, ipaddr)
6366823Swpaul	u_char *ep;
6376823Swpaul	u_long ipaddr;
6386823Swpaul{
6396866Swpaul#ifdef SIOCSARP
6406823Swpaul	int s;
6416823Swpaul	struct arpreq request;
6426823Swpaul	struct sockaddr_in *sin;
6436823Swpaul
6446823Swpaul	request.arp_flags = 0;
6456823Swpaul	sin = (struct sockaddr_in *)&request.arp_pa;
6466823Swpaul	sin->sin_family = AF_INET;
6476823Swpaul	sin->sin_addr.s_addr = ipaddr;
6486823Swpaul	request.arp_ha.sa_family = AF_UNSPEC;
6496823Swpaul	bcopy((char *)ep, (char *)request.arp_ha.sa_data, 6);
6506823Swpaul
6516823Swpaul	s = socket(AF_INET, SOCK_DGRAM, 0);
6526823Swpaul	if (ioctl(s, SIOCSARP, (caddr_t)&request) < 0)
6536823Swpaul		syslog(LOG_ERR, "SIOCSARP: %m");
6546823Swpaul	(void)close(s);
6556866Swpaul#else
6566866Swpaul	if (arptab_set(ep, ipaddr) > 0)
6576866Swpaul		syslog(LOG_ERR, "couldn't update arp table");
6586823Swpaul#endif
6596823Swpaul}
6606823Swpaul
6616823Swpaul/*
6626823Swpaul * Build a reverse ARP packet and sent it out on the interface.
6638857Srgrimes * 'ep' points to a valid ARPOP_REVREQUEST.  The ARPOP_REVREPLY is built
6646823Swpaul * on top of the request, then written to the network.
6656823Swpaul *
6666823Swpaul * RFC 903 defines the ether_arp fields as follows.  The following comments
6676823Swpaul * are taken (more or less) straight from this document.
6686823Swpaul *
6696866Swpaul * ARPOP_REVREQUEST
6706823Swpaul *
6716823Swpaul * arp_sha is the hardware address of the sender of the packet.
6726823Swpaul * arp_spa is undefined.
6736823Swpaul * arp_tha is the 'target' hardware address.
6746823Swpaul *   In the case where the sender wishes to determine his own
6756823Swpaul *   protocol address, this, like arp_sha, will be the hardware
6766823Swpaul *   address of the sender.
6776823Swpaul * arp_tpa is undefined.
6786823Swpaul *
6796866Swpaul * ARPOP_REVREPLY
6806823Swpaul *
6816823Swpaul * arp_sha is the hardware address of the responder (the sender of the
6826823Swpaul *   reply packet).
6836823Swpaul * arp_spa is the protocol address of the responder (see the note below).
6846823Swpaul * arp_tha is the hardware address of the target, and should be the same as
6856823Swpaul *   that which was given in the request.
6866823Swpaul * arp_tpa is the protocol address of the target, that is, the desired address.
6878857Srgrimes *
6886823Swpaul * Note that the requirement that arp_spa be filled in with the responder's
6898857Srgrimes * protocol is purely for convenience.  For instance, if a system were to use
6908857Srgrimes * both ARP and RARP, then the inclusion of the valid protocol-hardware
6918857Srgrimes * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent
6926823Swpaul * ARP request.
6936823Swpaul */
6946823Swpaulrarp_reply(ii, ep, ipaddr)
6956823Swpaul	struct if_info *ii;
6966823Swpaul	struct ether_header *ep;
6976823Swpaul	u_long ipaddr;
6986823Swpaul{
6996823Swpaul	int n;
7006823Swpaul	struct ether_arp *ap = (struct ether_arp *)(ep + 1);
7016823Swpaul	int len, raw_sock;
7026823Swpaul
7036823Swpaul	update_arptab((u_char *)&ap->arp_sha, ipaddr);
7046823Swpaul
7056823Swpaul	/*
7066823Swpaul	 * Build the rarp reply by modifying the rarp request in place.
7076823Swpaul	 */
7086823Swpaul	ap->arp_op = htons(ARPOP_REVREPLY);
7096823Swpaul
7106823Swpaul	/*
7116823Swpaul	 * XXX   Using htons(ETHERTYPE_REVARP) doesn't work: you wind
7126823Swpaul	 * up transmitting 0x3580 instead of the correct value of
7136823Swpaul	 * 0x8035. What makes no sense is that the NetBSD people
7146823Swpaul	 * do in fact use htons(ETHERTYPE_REVARP) in their rarpd.
7156823Swpaul	 * (Thank god for tcpdump or I would never have figured this
7166823Swpaul	 * out.)
7176823Swpaul	 */
7186823Swpaul	ep->ether_type = ETHERTYPE_REVARP;
7196823Swpaul
7206823Swpaul	bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6);
7216823Swpaul	bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6);
7226823Swpaul	bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6);
7236823Swpaul
7246823Swpaul	bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4);
7256823Swpaul	/* Target hardware is unchanged. */
7266823Swpaul	bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4);
7276823Swpaul
7286823Swpaul	len = sizeof(*ep) + sizeof(*ap);
7296823Swpaul	n = write(ii->ii_fd, (char *)ep, len);
7306823Swpaul	if (n != len) {
7316823Swpaul		syslog(LOG_ERR, "write: only %d of %d bytes written", n, len);
7326823Swpaul	}
7336823Swpaul}
7346823Swpaul
7356823Swpaul/*
7366823Swpaul * Get the netmask of an IP address.  This routine is used if
7376823Swpaul * SIOCGIFNETMASK doesn't work.
7386823Swpaul */
7396823Swpaulu_long
7406823Swpaulipaddrtonetmask(addr)
7416823Swpaul	u_long addr;
7426823Swpaul{
7436823Swpaul	if (IN_CLASSA(addr))
7446823Swpaul		return IN_CLASSA_NET;
7456823Swpaul	if (IN_CLASSB(addr))
7466823Swpaul		return IN_CLASSB_NET;
7476823Swpaul	if (IN_CLASSC(addr))
7486823Swpaul		return IN_CLASSC_NET;
7496823Swpaul	syslog(LOG_DEBUG, "unknown IP address class: %08X", addr);
7506823Swpaul	exit(1);
7516823Swpaul	/* NOTREACHED */
7526823Swpaul}
753