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