/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution. Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1990\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
/*
* The structure for each interface.
*/
struct if_info {
int ii_fd; /* BPF file descriptor */
u_char ii_eaddr[6]; /* Ethernet address of this interface */
u_int32_t ii_ipaddr; /* IP address of this interface */
u_int32_t ii_netmask; /* subnet or net mask */
char *ii_name; /* interface name */
struct if_info *ii_alias;
struct if_info *ii_next;
};
/*
* The list of all interfaces that are being listened to. rarp_loop()
* "selects" on the descriptors in this list.
*/
static struct if_info *iflist;
if ((!fflag) && (!dflag)) {
if (daemon(0, 0))
rarperr(FATAL, "daemon");
pidfile(NULL);
}
if (aflag)
init_some(NULL);
else {
while (argc--)
init_some(*argv++);
}
rarp_loop();
/* NOTREACHED */
return (0);
}
/*
* Add 'ifname' to the interface list. Lookup its IP address and network
* mask and Ethernet address, and open a BPF file for it.
*/
static void
init_one(char *ifname, u_int32_t ipaddr)
{
struct if_info *h;
struct if_info *p;
int fd;
for (h = iflist; h != NULL; h = h->ii_next) {
if (!strcmp(h->ii_name, ifname))
break;
}
if (h == NULL) {
fd = rarp_open(ifname);
if (fd < 0)
return;
} else {
fd = h->ii_fd;
}
/*
* Initialize all "candidate" interfaces that are in the system
* configuration list. A "candidate" is up, not loopback and not
* point to point.
*/
static void
init_some(char *name)
{
struct ifaddrs *ifap, *ifa, *p;
static int
bpf_open(void)
{
int fd;
const char *device = _PATH_BPF;
fd = open(device, O_RDWR);
if (fd < 0) {
rarperr(FATAL, "%s: %s", device, strerror(errno));
/* NOTREACHED */
}
return fd;
}
/*
* Open a BPF file and attach it to the interface named 'device'.
* Set immediate mode, and set a filter that accepts only RARP requests.
*/
static int
rarp_open(char *device)
{
int fd;
struct ifreq ifr;
u_int dlt;
int immediate;
u_int bufsize;
/*
* True if this server can boot the host whose IP address is 'addr'.
* This check is made by looking in the tftp directory for the
* configuration file.
*/
static int
rarp_bootable(u_int32_t addr)
{
struct dirent *dent;
DIR *d;
char ipname[9];
static DIR *dd = 0;
(void)snprintf(ipname, sizeof(ipname), "%08X", addr);
/* If directory is already open, rewind it. Otherwise, open it. */
if (d = dd)
rewinddir(d);
else {
if (chdir(TFTP_DIR) == -1) {
rarperr(FATAL, "chdir: %s", strerror(errno));
/* NOTREACHED */
}
d = opendir(".");
if (d == 0) {
rarperr(FATAL, "opendir: %s", strerror(errno));
/* NOTREACHED */
}
dd = d;
}
while (dent = readdir(d))
if (strncmp(dent->d_name, ipname, 8) == 0)
return 1;
return 0;
}
#endif /* REQUIRE_TFTPBOOT */
/*
* Given a list of IP addresses, 'alist', return the first address that
* is on network 'net'; 'netmask' is a mask indicating the network portion
* of the address.
*/
static u_int32_t
choose_ipaddr(u_int32_t **alist, u_int32_t net, u_int32_t netmask)
{
for (; *alist; ++alist) {
if ((**alist & netmask) == net)
return **alist;
}
return 0;
}
/*
* Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has
* already been checked for validity. The reply is overlaid on the request.
*/
static void
rarp_process(struct if_info *ii, u_char *pkt)
{
struct ether_header *ep;
struct hostent *hp;
u_int32_t target_ipaddr = 0;
char ename[MAXHOSTNAMELEN + 1];
struct in_addr in;
ep = (struct ether_header *) pkt;
if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0) {
debug("no IP address for %s",
ether_ntoa((struct ether_addr *)&ep->ether_shost));
return;
}
ename[sizeof(ename)-1] = '\0';
(void)close(fd);
}
/*
* Poke the kernel arp tables with the ethernet/ip address combination
* given. When processing a reply, we must do this so that the booting
* host (i.e. the guy running rarpd), won't try to ARP for the hardware
* address of the guy being booted (he cannot answer the ARP).
*/
#ifndef __NetBSD__
static void
update_arptab(u_char *ep, u_int32_t ipaddr)
{
struct arpreq request;
struct sockaddr_in *sin;
request.arp_flags = 0;
sin = (struct sockaddr_in *) & request.arp_pa;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipaddr;
request.arp_ha.sa_family = AF_UNSPEC;
/* This is needed #if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN,
because AF_UNSPEC is zero and the kernel assumes that a zero
sa_family means that the real sa_family value is in sa_len. */
request.arp_ha.sa_len = 16; /* XXX */
memmove((char *) request.arp_ha.sa_data, (char *)ep, 6);
/*
* Build a reverse ARP packet and sent it out on the interface.
* 'ep' points to a valid ARPOP_REVREQUEST. The ARPOP_REVREPLY is built
* on top of the request, then written to the network.
*
* RFC 903 defines the ether_arp fields as follows. The following comments
* are taken (more or less) straight from this document.
*
* ARPOP_REVREQUEST
*
* arp_sha is the hardware address of the sender of the packet.
* arp_spa is undefined.
* arp_tha is the 'target' hardware address.
* In the case where the sender wishes to determine his own
* protocol address, this, like arp_sha, will be the hardware
* address of the sender.
* arp_tpa is undefined.
*
* ARPOP_REVREPLY
*
* arp_sha is the hardware address of the responder (the sender of the
* reply packet).
* arp_spa is the protocol address of the responder (see the note below).
* arp_tha is the hardware address of the target, and should be the same as
* that which was given in the request.
* arp_tpa is the protocol address of the target, that is, the desired address.
*
* Note that the requirement that arp_spa be filled in with the responder's
* protocol is purely for convenience. For instance, if a system were to use
* both ARP and RARP, then the inclusion of the valid protocol-hardware
* address pair (arp_spa, arp_sha) may eliminate the need for a subsequent
* ARP request.
*/
static void
rarp_reply(struct if_info *ii, struct ether_header *ep, u_int32_t ipaddr,
struct hostent *hp)
{
int n;
#ifdef __NetBSD__
struct arphdr *ap = (struct arphdr *) (ep + 1);
#else
struct ether_arp *ap = (struct ether_arp *) (ep + 1);
#endif
debug("%s asked; %s replied",
ether_ntoa((struct ether_addr *)ar_tha(ap)), hp->h_name);
if (lflag)
syslog(LOG_INFO, "%s asked; %s replied",
ether_ntoa((struct ether_addr *)ar_tha(ap)), hp->h_name);
n = write(ii->ii_fd, (char *) ep, len);
if (n != len) {
rarperr(NONFATAL, "write: only %d of %d bytes written", n, len);
}
}
/*
* Get the netmask of an IP address. This routine is used if
* SIOCGIFNETMASK doesn't work.
*/
static u_int32_t
ipaddrtonetmask(u_int32_t addr)
{
if (IN_CLASSA(addr))
return IN_CLASSA_NET;
if (IN_CLASSB(addr))
return IN_CLASSB_NET;
if (IN_CLASSC(addr))
return IN_CLASSC_NET;
rarperr(FATAL, "unknown IP address class: %08X", addr);
/* NOTREACHED */
return(-1);
}