16823Swpaul/* 219859Swpaul * Copyright (c) 1990, 1991, 1992, 1993, 1996 319859Swpaul * The Regents of the University of California. 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 10133249Simp * provided with the distribution 11133249Simp * 126823Swpaul * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 136823Swpaul * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 146823Swpaul * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 156823Swpaul */ 1630372Scharnier 17114601Sobrien#if 0 186823Swpaul#ifndef lint 1930372Scharnierstatic const char copyright[] = 2019859Swpaul"@(#) Copyright (c) 1990, 1991, 1992, 1993, 1996\n\ 2119859SwpaulThe Regents of the University of California. All rights reserved.\n"; 226823Swpaul#endif /* not lint */ 23114601Sobrien#endif 24114601Sobrien#include <sys/cdefs.h> 25114601Sobrien__FBSDID("$FreeBSD: releng/10.3/usr.sbin/rarpd/rarpd.c 249234 2013-04-07 15:33:06Z marius $"); 266823Swpaul 278857Srgrimes/* 286823Swpaul * rarpd - Reverse ARP Daemon 296823Swpaul * 30238282Shrs * Usage: rarpd -a [-dfsv] [-t directory] [-P pidfile] [hostname] 31238282Shrs * rarpd [-dfsv] [-t directory] [-P pidfile] interface [hostname] 328857Srgrimes * 336823Swpaul * 'hostname' is optional solely for backwards compatibility with Sun's rarpd. 346823Swpaul * Currently, the argument is ignored. 356823Swpaul */ 3619859Swpaul#include <sys/param.h> 3719859Swpaul#include <sys/file.h> 3819859Swpaul#include <sys/ioctl.h> 3919859Swpaul#include <sys/socket.h> 4019859Swpaul#include <sys/time.h> 416823Swpaul 426823Swpaul#include <net/bpf.h> 4378057Sroam#include <net/ethernet.h> 446823Swpaul#include <net/if.h> 4519859Swpaul#include <net/if_types.h> 4619859Swpaul#include <net/if_dl.h> 4719859Swpaul#include <net/route.h> 4819859Swpaul 496823Swpaul#include <netinet/in.h> 506823Swpaul#include <netinet/if_ether.h> 5119859Swpaul 5219859Swpaul#include <arpa/inet.h> 5319859Swpaul 54117446Smux#include <dirent.h> 5519859Swpaul#include <errno.h> 56117446Smux#include <ifaddrs.h> 576823Swpaul#include <netdb.h> 5878402Sroam#include <stdarg.h> 5919859Swpaul#include <stdio.h> 6019859Swpaul#include <string.h> 6119859Swpaul#include <syslog.h> 6219859Swpaul#include <stdlib.h> 6319859Swpaul#include <unistd.h> 64238282Shrs#include <libutil.h> 656823Swpaul 66128469Sjoerg/* Cast a struct sockaddr to a struct sockaddr_in */ 6719859Swpaul#define SATOSIN(sa) ((struct sockaddr_in *)(sa)) 6819859Swpaul 6919859Swpaul#ifndef TFTP_DIR 7019859Swpaul#define TFTP_DIR "/tftpboot" 7119859Swpaul#endif 7219859Swpaul 7319859Swpaul#define ARPSECS (20 * 60) /* as per code in netinet/if_ether.c */ 7419859Swpaul#define REVARP_REQUEST ARPOP_REVREQUEST 7519859Swpaul#define REVARP_REPLY ARPOP_REVREPLY 7619859Swpaul 776823Swpaul/* 788857Srgrimes * The structure for each interface. 796823Swpaul */ 806823Swpaulstruct if_info { 81117446Smux struct if_info *ii_next; 82117446Smux int ii_fd; /* BPF file descriptor */ 83117446Smux in_addr_t ii_ipaddr; /* IP address */ 84117446Smux in_addr_t ii_netmask; /* subnet or net mask */ 85117446Smux u_char ii_eaddr[ETHER_ADDR_LEN]; /* ethernet address */ 86117446Smux char ii_ifname[IF_NAMESIZE]; 876823Swpaul}; 886823Swpaul 896823Swpaul/* 906823Swpaul * The list of all interfaces that are being listened to. rarp_loop() 916823Swpaul * "selects" on the descriptors in this list. 926823Swpaul */ 93249234Smariusstatic struct if_info *iflist; 946823Swpaul 95249234Smariusstatic int verbose; /* verbose messages */ 96249234Smariusstatic const char *tftp_dir = TFTP_DIR; /* tftp directory */ 976823Swpaul 98249234Smariusstatic int dflag; /* messages to stdout/stderr, not syslog(3) */ 99249234Smariusstatic int sflag; /* ignore /tftpboot */ 10019859Swpaul 10119859Swpaulstatic u_char zero[6]; 10219859Swpaul 103238282Shrsstatic char pidfile_buf[PATH_MAX]; 104238282Shrsstatic char *pidfile; 105238282Shrs#define RARPD_PIDFILE "/var/run/rarpd.%s.pid" 106238282Shrsstatic struct pidfh *pidfile_fh; 107238282Shrs 10878057Sroamstatic int bpf_open(void); 109116369Sjmgstatic in_addr_t choose_ipaddr(in_addr_t **, in_addr_t, in_addr_t); 11078057Sroamstatic char *eatoa(u_char *); 11178402Sroamstatic int expand_syslog_m(const char *fmt, char **newfmt); 11278057Sroamstatic void init(char *); 113128474Sjoergstatic void init_one(struct ifaddrs *, char *, int); 114116369Sjmgstatic char *intoa(in_addr_t); 115116369Sjmgstatic in_addr_t ipaddrtonetmask(in_addr_t); 11678402Sroamstatic void logmsg(int, const char *, ...) __printflike(2, 3); 117116369Sjmgstatic int rarp_bootable(in_addr_t); 11878057Sroamstatic int rarp_check(u_char *, u_int); 11978057Sroamstatic void rarp_loop(void); 12078057Sroamstatic int rarp_open(char *); 12178057Sroamstatic void rarp_process(struct if_info *, u_char *, u_int); 12278402Sroamstatic void rarp_reply(struct if_info *, struct ether_header *, 123116369Sjmg in_addr_t, u_int); 124116369Sjmgstatic void update_arptab(u_char *, in_addr_t); 12578057Sroamstatic void usage(void); 12619986Sfenner 12751287Speterint 12878057Sroammain(int argc, char *argv[]) 1296823Swpaul{ 13019859Swpaul int op; 131230346Seadler char *ifname, *name; 1326823Swpaul 1336823Swpaul int aflag = 0; /* listen on "all" interfaces */ 1346823Swpaul int fflag = 0; /* don't fork */ 1356823Swpaul 13619859Swpaul if ((name = strrchr(argv[0], '/')) != NULL) 1376823Swpaul ++name; 1386823Swpaul else 1396823Swpaul name = argv[0]; 1406823Swpaul if (*name == '-') 1416823Swpaul ++name; 1426823Swpaul 1438857Srgrimes /* 14478402Sroam * All error reporting is done through syslog, unless -d is specified 1456823Swpaul */ 14619859Swpaul openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON); 1476823Swpaul 1486823Swpaul opterr = 0; 149238282Shrs while ((op = getopt(argc, argv, "adfsP:t:v")) != -1) 1506823Swpaul switch (op) { 1516823Swpaul case 'a': 1526823Swpaul ++aflag; 1536823Swpaul break; 1546823Swpaul 15578402Sroam case 'd': 15678402Sroam ++dflag; 15778402Sroam break; 15878402Sroam 1596823Swpaul case 'f': 1606823Swpaul ++fflag; 1616823Swpaul break; 1626823Swpaul 16319986Sfenner case 's': 16419986Sfenner ++sflag; 16519986Sfenner break; 16619986Sfenner 167238282Shrs case 'P': 168238282Shrs strncpy(pidfile_buf, optarg, sizeof(pidfile_buf) - 1); 169238282Shrs pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; 170238282Shrs pidfile = pidfile_buf; 171238282Shrs break; 172238282Shrs 17386460Srwatson case 't': 17486460Srwatson tftp_dir = optarg; 17586460Srwatson break; 17686460Srwatson 17719859Swpaul case 'v': 17819859Swpaul ++verbose; 17919859Swpaul break; 18019859Swpaul 1816823Swpaul default: 1826823Swpaul usage(); 1836823Swpaul /* NOTREACHED */ 1846823Swpaul } 185119003Scharnier argc -= optind; 186119003Scharnier argv += optind; 187119003Scharnier 188119003Scharnier ifname = (aflag == 0) ? argv[0] : NULL; 189119003Scharnier 19019859Swpaul if ((aflag && ifname) || (!aflag && ifname == NULL)) 1916823Swpaul usage(); 1926823Swpaul 193119003Scharnier init(ifname); 1948857Srgrimes 19519859Swpaul if (!fflag) { 196238282Shrs if (pidfile == NULL && ifname != NULL && aflag == 0) { 197238282Shrs snprintf(pidfile_buf, sizeof(pidfile_buf) - 1, 198238282Shrs RARPD_PIDFILE, ifname); 199238282Shrs pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; 200238282Shrs pidfile = pidfile_buf; 201238282Shrs } 202238282Shrs /* If pidfile == NULL, /var/run/<progname>.pid will be used. */ 203238282Shrs pidfile_fh = pidfile_open(pidfile, 0600, NULL); 204238282Shrs if (pidfile_fh == NULL) 205238282Shrs logmsg(LOG_ERR, "Cannot open or create pidfile: %s", 206238282Shrs (pidfile == NULL) ? "/var/run/rarpd.pid" : pidfile); 2079577Swpaul if (daemon(0,0)) { 20878402Sroam logmsg(LOG_ERR, "cannot fork"); 209238282Shrs pidfile_remove(pidfile_fh); 21019859Swpaul exit(1); 2116823Swpaul } 212238282Shrs pidfile_write(pidfile_fh); 21319859Swpaul } 2146823Swpaul rarp_loop(); 21553766Scharnier return(0); 2166823Swpaul} 2176823Swpaul 2186823Swpaul/* 21919859Swpaul * Add to the interface list. 2206823Swpaul */ 221117446Smuxstatic void 222128474Sjoerginit_one(struct ifaddrs *ifa, char *target, int pass1) 2236823Swpaul{ 224128474Sjoerg struct if_info *ii, *ii2; 22578057Sroam struct sockaddr_dl *ll; 22619859Swpaul int family; 2276823Swpaul 228117446Smux family = ifa->ifa_addr->sa_family; 22919859Swpaul switch (family) { 23019859Swpaul case AF_INET: 231128474Sjoerg if (pass1) 232128474Sjoerg /* Consider only AF_LINK during pass1. */ 233128474Sjoerg return; 234128474Sjoerg /* FALLTHROUGH */ 23519859Swpaul case AF_LINK: 236117446Smux if (!(ifa->ifa_flags & IFF_UP) || 237117446Smux (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) 23819859Swpaul return; 23919859Swpaul break; 24019859Swpaul default: 24119859Swpaul return; 24219859Swpaul } 24319859Swpaul 24419859Swpaul /* Don't bother going any further if not the target interface */ 245117446Smux if (target != NULL && strcmp(ifa->ifa_name, target) != 0) 24619859Swpaul return; 24719859Swpaul 24819859Swpaul /* Look for interface in list */ 24919859Swpaul for (ii = iflist; ii != NULL; ii = ii->ii_next) 250117446Smux if (strcmp(ifa->ifa_name, ii->ii_ifname) == 0) 25119859Swpaul break; 25219859Swpaul 253128474Sjoerg if (pass1 && ii != NULL) 254128474Sjoerg /* We've already seen that interface once. */ 255128474Sjoerg return; 256128474Sjoerg 25719859Swpaul /* Allocate a new one if not found */ 25819859Swpaul if (ii == NULL) { 25919859Swpaul ii = (struct if_info *)malloc(sizeof(*ii)); 26019859Swpaul if (ii == NULL) { 26178402Sroam logmsg(LOG_ERR, "malloc: %m"); 262238282Shrs pidfile_remove(pidfile_fh); 26319859Swpaul exit(1); 26419859Swpaul } 26519859Swpaul bzero(ii, sizeof(*ii)); 26619859Swpaul ii->ii_fd = -1; 267117446Smux strlcpy(ii->ii_ifname, ifa->ifa_name, sizeof(ii->ii_ifname)); 26819859Swpaul ii->ii_next = iflist; 26919859Swpaul iflist = ii; 270128474Sjoerg } else if (!pass1 && ii->ii_ipaddr != 0) { 271128474Sjoerg /* 272128474Sjoerg * Second AF_INET definition for that interface: clone 273128474Sjoerg * the existing one, and work on that cloned one. 274128474Sjoerg * This must be another IP address for this interface, 275128474Sjoerg * so avoid killing the previous configuration. 276128474Sjoerg */ 277128474Sjoerg ii2 = (struct if_info *)malloc(sizeof(*ii2)); 278128474Sjoerg if (ii2 == NULL) { 279128474Sjoerg logmsg(LOG_ERR, "malloc: %m"); 280238282Shrs pidfile_remove(pidfile_fh); 281128474Sjoerg exit(1); 282128474Sjoerg } 283128474Sjoerg memcpy(ii2, ii, sizeof(*ii2)); 284128474Sjoerg ii2->ii_fd = -1; 285128474Sjoerg ii2->ii_next = iflist; 286128474Sjoerg iflist = ii2; 287128474Sjoerg 288128474Sjoerg ii = ii2; 28919859Swpaul } 29019859Swpaul 29119859Swpaul switch (family) { 29219859Swpaul case AF_INET: 293117446Smux ii->ii_ipaddr = SATOSIN(ifa->ifa_addr)->sin_addr.s_addr; 294117446Smux ii->ii_netmask = SATOSIN(ifa->ifa_netmask)->sin_addr.s_addr; 29519859Swpaul if (ii->ii_netmask == 0) 29619859Swpaul ii->ii_netmask = ipaddrtonetmask(ii->ii_ipaddr); 297117446Smux if (ii->ii_fd < 0) 29819859Swpaul ii->ii_fd = rarp_open(ii->ii_ifname); 29919859Swpaul break; 30019859Swpaul 301117446Smux case AF_LINK: 302117446Smux ll = (struct sockaddr_dl *)ifa->ifa_addr; 303238282Shrs switch (ll->sdl_type) { 304238282Shrs case IFT_ETHER: 305238282Shrs case IFT_L2VLAN: 306117446Smux bcopy(LLADDR(ll), ii->ii_eaddr, 6); 307238282Shrs } 308117446Smux break; 309117446Smux } 3106823Swpaul} 311249234Smarius 3126823Swpaul/* 3136823Swpaul * Initialize all "candidate" interfaces that are in the system 3146823Swpaul * configuration list. A "candidate" is up, not loopback and not 3156823Swpaul * point to point. 3166823Swpaul */ 317117446Smuxstatic void 31878057Sroaminit(char *target) 3196823Swpaul{ 32078057Sroam struct if_info *ii, *nii, *lii; 321117446Smux struct ifaddrs *ifhead, *ifa; 322117446Smux int error; 3236823Swpaul 324117446Smux error = getifaddrs(&ifhead); 325117446Smux if (error) { 326117446Smux logmsg(LOG_ERR, "getifaddrs: %m"); 327238282Shrs pidfile_remove(pidfile_fh); 3286823Swpaul exit(1); 3296823Swpaul } 330128474Sjoerg /* 331128474Sjoerg * We make two passes over the list we have got. In the first 332128474Sjoerg * one, we only collect AF_LINK interfaces, and initialize our 333128474Sjoerg * list of interfaces from them. In the second pass, we 334128474Sjoerg * collect the actual IP addresses from the AF_INET 335128474Sjoerg * interfaces, and allow for the same interface name to appear 336128474Sjoerg * multiple times (in case of more than one IP address). 337128474Sjoerg */ 338117446Smux for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next) 339128474Sjoerg init_one(ifa, target, 1); 340128474Sjoerg for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next) 341128474Sjoerg init_one(ifa, target, 0); 342117446Smux freeifaddrs(ifhead); 34319859Swpaul 34419859Swpaul /* Throw away incomplete interfaces */ 34519859Swpaul lii = NULL; 34619859Swpaul for (ii = iflist; ii != NULL; ii = nii) { 34719859Swpaul nii = ii->ii_next; 34819859Swpaul if (ii->ii_ipaddr == 0 || 34919859Swpaul bcmp(ii->ii_eaddr, zero, 6) == 0) { 35019859Swpaul if (lii == NULL) 35119859Swpaul iflist = nii; 35219859Swpaul else 35319859Swpaul lii->ii_next = nii; 35419859Swpaul if (ii->ii_fd >= 0) 35519859Swpaul close(ii->ii_fd); 35619859Swpaul free(ii); 35719859Swpaul continue; 3586823Swpaul } 35919859Swpaul lii = ii; 3606823Swpaul } 36119859Swpaul 36219859Swpaul /* Verbose stuff */ 36319859Swpaul if (verbose) 36419859Swpaul for (ii = iflist; ii != NULL; ii = ii->ii_next) 365116369Sjmg logmsg(LOG_DEBUG, "%s %s 0x%08x %s", 36619889Swpaul ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)), 367116369Sjmg (in_addr_t)ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr)); 3686823Swpaul} 3696823Swpaul 370117446Smuxstatic void 37178057Sroamusage(void) 3726823Swpaul{ 373249234Smarius 374119003Scharnier (void)fprintf(stderr, "%s\n%s\n", 375238282Shrs "usage: rarpd -a [-dfsv] [-t directory] [-P pidfile]", 376238282Shrs " rarpd [-dfsv] [-t directory] [-P pidfile] interface"); 3776823Swpaul exit(1); 3786823Swpaul} 3796823Swpaul 380117446Smuxstatic int 38178057Sroambpf_open(void) 3826823Swpaul{ 3836823Swpaul int fd; 3846823Swpaul int n = 0; 3856823Swpaul char device[sizeof "/dev/bpf000"]; 3866823Swpaul 3876823Swpaul /* 3888857Srgrimes * Go through all the minors and find one that isn't in use. 3896823Swpaul */ 3906823Swpaul do { 3916823Swpaul (void)sprintf(device, "/dev/bpf%d", n++); 3926823Swpaul fd = open(device, O_RDWR); 39378057Sroam } while ((fd == -1) && (errno == EBUSY)); 3946823Swpaul 39578057Sroam if (fd == -1) { 39678402Sroam logmsg(LOG_ERR, "%s: %m", device); 397238282Shrs pidfile_remove(pidfile_fh); 39819859Swpaul exit(1); 3996823Swpaul } 4006823Swpaul return fd; 4016823Swpaul} 4026823Swpaul 4036823Swpaul/* 4046823Swpaul * Open a BPF file and attach it to the interface named 'device'. 4056823Swpaul * Set immediate mode, and set a filter that accepts only RARP requests. 4066823Swpaul */ 407117446Smuxstatic int 40878057Sroamrarp_open(char *device) 4096823Swpaul{ 4106823Swpaul int fd; 4116823Swpaul struct ifreq ifr; 41219859Swpaul u_int dlt; 41319859Swpaul int immediate; 4146823Swpaul 4156823Swpaul static struct bpf_insn insns[] = { 41619859Swpaul BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12), 41719859Swpaul BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3), 41819859Swpaul BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20), 41919859Swpaul BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1), 42019859Swpaul BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) + 42119859Swpaul sizeof(struct ether_header)), 42219859Swpaul BPF_STMT(BPF_RET|BPF_K, 0), 42319859Swpaul }; 42419859Swpaul static struct bpf_program filter = { 42519859Swpaul sizeof insns / sizeof(insns[0]), 42619859Swpaul insns 42719859Swpaul }; 4286823Swpaul 4296823Swpaul fd = bpf_open(); 4308857Srgrimes /* 4316823Swpaul * Set immediate mode so packets are processed as they arrive. 4326823Swpaul */ 4336823Swpaul immediate = 1; 43478057Sroam if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) { 43578402Sroam logmsg(LOG_ERR, "BIOCIMMEDIATE: %m"); 436238282Shrs goto rarp_open_err; 4376823Swpaul } 438117446Smux strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); 43978057Sroam if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) { 44078402Sroam logmsg(LOG_ERR, "BIOCSETIF: %m"); 441238282Shrs goto rarp_open_err; 4426823Swpaul } 4436823Swpaul /* 4446823Swpaul * Check that the data link layer is an Ethernet; this code won't 4456823Swpaul * work with anything else. 4466823Swpaul */ 44778057Sroam if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) { 44878402Sroam logmsg(LOG_ERR, "BIOCGDLT: %m"); 449238282Shrs goto rarp_open_err; 4506823Swpaul } 45119859Swpaul if (dlt != DLT_EN10MB) { 45278402Sroam logmsg(LOG_ERR, "%s is not an ethernet", device); 453238282Shrs goto rarp_open_err; 4546823Swpaul } 4556823Swpaul /* 4566823Swpaul * Set filter program. 4576823Swpaul */ 45878057Sroam if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) { 45978402Sroam logmsg(LOG_ERR, "BIOCSETF: %m"); 460238282Shrs goto rarp_open_err; 4616823Swpaul } 4626823Swpaul return fd; 463238282Shrs 464238282Shrsrarp_open_err: 465238282Shrs pidfile_remove(pidfile_fh); 466238282Shrs exit(1); 4676823Swpaul} 4686823Swpaul 4696823Swpaul/* 4706823Swpaul * Perform various sanity checks on the RARP request packet. Return 4716823Swpaul * false on failure and log the reason. 4726823Swpaul */ 473117446Smuxstatic int 47478057Sroamrarp_check(u_char *p, u_int len) 4756823Swpaul{ 4766823Swpaul struct ether_header *ep = (struct ether_header *)p; 4776823Swpaul struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep)); 4786823Swpaul 4796823Swpaul if (len < sizeof(*ep) + sizeof(*ap)) { 48078402Sroam logmsg(LOG_ERR, "truncated request, got %u, expected %lu", 48178357Sjlemon len, (u_long)(sizeof(*ep) + sizeof(*ap))); 4826823Swpaul return 0; 4836823Swpaul } 4846823Swpaul /* 4856823Swpaul * XXX This test might be better off broken out... 4866823Swpaul */ 48719859Swpaul if (ntohs(ep->ether_type) != ETHERTYPE_REVARP || 48819859Swpaul ntohs(ap->arp_hrd) != ARPHRD_ETHER || 48919859Swpaul ntohs(ap->arp_op) != REVARP_REQUEST || 49019859Swpaul ntohs(ap->arp_pro) != ETHERTYPE_IP || 4916823Swpaul ap->arp_hln != 6 || ap->arp_pln != 4) { 49278402Sroam logmsg(LOG_DEBUG, "request fails sanity check"); 4936823Swpaul return 0; 4946823Swpaul } 4956823Swpaul if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) { 49678402Sroam logmsg(LOG_DEBUG, "ether/arp sender address mismatch"); 4976823Swpaul return 0; 4986823Swpaul } 4996823Swpaul if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) { 50078402Sroam logmsg(LOG_DEBUG, "ether/arp target address mismatch"); 5016823Swpaul return 0; 5026823Swpaul } 5036823Swpaul return 1; 5046823Swpaul} 5056823Swpaul 5066823Swpaul/* 5078857Srgrimes * Loop indefinitely listening for RARP requests on the 5086823Swpaul * interfaces in 'iflist'. 5096823Swpaul */ 510117446Smuxstatic void 51178057Sroamrarp_loop(void) 5126823Swpaul{ 51319859Swpaul u_char *buf, *bp, *ep; 5146823Swpaul int cc, fd; 5156823Swpaul fd_set fds, listeners; 5166823Swpaul int bufsize, maxfd = 0; 5176823Swpaul struct if_info *ii; 5186823Swpaul 51919859Swpaul if (iflist == NULL) { 52078402Sroam logmsg(LOG_ERR, "no interfaces"); 521238282Shrs goto rarpd_loop_err; 5226823Swpaul } 52378057Sroam if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) { 52478402Sroam logmsg(LOG_ERR, "BIOCGBLEN: %m"); 525238282Shrs goto rarpd_loop_err; 5266823Swpaul } 52778163Sbde buf = malloc(bufsize); 52819859Swpaul if (buf == NULL) { 52978402Sroam logmsg(LOG_ERR, "malloc: %m"); 530238282Shrs goto rarpd_loop_err; 53119859Swpaul } 5326823Swpaul 53319859Swpaul while (1) { 53419889Swpaul /* 53519889Swpaul * Find the highest numbered file descriptor for select(). 53619889Swpaul * Initialize the set of descriptors to listen to. 53719889Swpaul */ 53819859Swpaul FD_ZERO(&fds); 53919859Swpaul for (ii = iflist; ii != NULL; ii = ii->ii_next) { 54019859Swpaul FD_SET(ii->ii_fd, &fds); 54119859Swpaul if (ii->ii_fd > maxfd) 54219859Swpaul maxfd = ii->ii_fd; 54319859Swpaul } 5446823Swpaul listeners = fds; 54578057Sroam if (select(maxfd + 1, &listeners, NULL, NULL, NULL) == -1) { 54619859Swpaul /* Don't choke when we get ptraced */ 54719859Swpaul if (errno == EINTR) 54819859Swpaul continue; 54978402Sroam logmsg(LOG_ERR, "select: %m"); 550238282Shrs goto rarpd_loop_err; 5516823Swpaul } 55219859Swpaul for (ii = iflist; ii != NULL; ii = ii->ii_next) { 5536823Swpaul fd = ii->ii_fd; 55419859Swpaul if (!FD_ISSET(fd, &listeners)) 55519859Swpaul continue; 55619859Swpaul again: 55719859Swpaul cc = read(fd, (char *)buf, bufsize); 55819859Swpaul /* Don't choke when we get ptraced */ 55978057Sroam if ((cc == -1) && (errno == EINTR)) 56019859Swpaul goto again; 5616823Swpaul 56219859Swpaul /* Loop through the packet(s) */ 56319859Swpaul#define bhp ((struct bpf_hdr *)bp) 56419859Swpaul bp = buf; 56519859Swpaul ep = bp + cc; 56619859Swpaul while (bp < ep) { 56778057Sroam u_int caplen, hdrlen; 56819859Swpaul 56919859Swpaul caplen = bhp->bh_caplen; 57019859Swpaul hdrlen = bhp->bh_hdrlen; 57119859Swpaul if (rarp_check(bp + hdrlen, caplen)) 57219985Sfenner rarp_process(ii, bp + hdrlen, caplen); 57319859Swpaul bp += BPF_WORDALIGN(hdrlen + caplen); 5746823Swpaul } 5756823Swpaul } 5766823Swpaul } 57719859Swpaul#undef bhp 578238282Shrs return; 579238282Shrs 580238282Shrsrarpd_loop_err: 581238282Shrs pidfile_remove(pidfile_fh); 582238282Shrs exit(1); 5836823Swpaul} 5846823Swpaul 5856823Swpaul/* 5866823Swpaul * True if this server can boot the host whose IP address is 'addr'. 5876823Swpaul * This check is made by looking in the tftp directory for the 5886823Swpaul * configuration file. 5896823Swpaul */ 590117446Smuxstatic int 591116369Sjmgrarp_bootable(in_addr_t addr) 5926823Swpaul{ 59378057Sroam struct dirent *dent; 59478057Sroam DIR *d; 5956823Swpaul char ipname[9]; 59619859Swpaul static DIR *dd = NULL; 5976823Swpaul 598116369Sjmg (void)sprintf(ipname, "%08X", (in_addr_t)ntohl(addr)); 59919859Swpaul 6006823Swpaul /* 6016823Swpaul * If directory is already open, rewind it. Otherwise, open it. 6026823Swpaul */ 60319859Swpaul if ((d = dd) != NULL) 6046823Swpaul rewinddir(d); 6056823Swpaul else { 60619859Swpaul if (chdir(tftp_dir) == -1) { 60778402Sroam logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir); 608238282Shrs goto rarp_bootable_err; 6096823Swpaul } 6106823Swpaul d = opendir("."); 61119859Swpaul if (d == NULL) { 61278402Sroam logmsg(LOG_ERR, "opendir: %m"); 613238282Shrs goto rarp_bootable_err; 6146823Swpaul } 6156823Swpaul dd = d; 6166823Swpaul } 61719859Swpaul while ((dent = readdir(d)) != NULL) 6186823Swpaul if (strncmp(dent->d_name, ipname, 8) == 0) 6196823Swpaul return 1; 6206823Swpaul return 0; 621238282Shrs 622238282Shrsrarp_bootable_err: 623238282Shrs pidfile_remove(pidfile_fh); 624238282Shrs exit(1); 6256823Swpaul} 6266823Swpaul 6276823Swpaul/* 6286823Swpaul * Given a list of IP addresses, 'alist', return the first address that 6296823Swpaul * is on network 'net'; 'netmask' is a mask indicating the network portion 6306823Swpaul * of the address. 6316823Swpaul */ 632117446Smuxstatic in_addr_t 633116369Sjmgchoose_ipaddr(in_addr_t **alist, in_addr_t net, in_addr_t netmask) 6346823Swpaul{ 635249234Smarius 63619859Swpaul for (; *alist; ++alist) 6376823Swpaul if ((**alist & netmask) == net) 6386823Swpaul return **alist; 6396823Swpaul return 0; 6406823Swpaul} 6416823Swpaul 6426823Swpaul/* 6436823Swpaul * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has 6446823Swpaul * already been checked for validity. The reply is overlaid on the request. 6456823Swpaul */ 646117446Smuxstatic void 64778057Sroamrarp_process(struct if_info *ii, u_char *pkt, u_int len) 6486823Swpaul{ 6496823Swpaul struct ether_header *ep; 6506823Swpaul struct hostent *hp; 651116369Sjmg in_addr_t target_ipaddr; 6526823Swpaul char ename[256]; 6536823Swpaul 6546823Swpaul ep = (struct ether_header *)pkt; 65519889Swpaul /* should this be arp_tha? */ 65678057Sroam if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0) { 65778402Sroam logmsg(LOG_ERR, "cannot map %s to name", 65819985Sfenner eatoa(ep->ether_shost)); 65919859Swpaul return; 66019985Sfenner } 66119859Swpaul 66219985Sfenner if ((hp = gethostbyname(ename)) == NULL) { 66378402Sroam logmsg(LOG_ERR, "cannot map %s to IP address", ename); 66419859Swpaul return; 66519985Sfenner } 66619985Sfenner 6676823Swpaul /* 66819859Swpaul * Choose correct address from list. 6696823Swpaul */ 67019859Swpaul if (hp->h_addrtype != AF_INET) { 67178402Sroam logmsg(LOG_ERR, "cannot handle non IP addresses for %s", 67219889Swpaul ename); 67319889Swpaul return; 6746823Swpaul } 675116369Sjmg target_ipaddr = choose_ipaddr((in_addr_t **)hp->h_addr_list, 67619859Swpaul ii->ii_ipaddr & ii->ii_netmask, 67719889Swpaul ii->ii_netmask); 67819859Swpaul if (target_ipaddr == 0) { 67978402Sroam logmsg(LOG_ERR, "cannot find %s on net %s", 68019889Swpaul ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask))); 68119859Swpaul return; 68219859Swpaul } 68319986Sfenner if (sflag || rarp_bootable(target_ipaddr)) 68419859Swpaul rarp_reply(ii, ep, target_ipaddr, len); 68519859Swpaul else if (verbose > 1) 68678402Sroam logmsg(LOG_INFO, "%s %s at %s DENIED (not bootable)", 68719859Swpaul ii->ii_ifname, 68819889Swpaul eatoa(ep->ether_shost), 68919889Swpaul intoa(ntohl(target_ipaddr))); 6906823Swpaul} 6916823Swpaul 6926823Swpaul/* 69319859Swpaul * Poke the kernel arp tables with the ethernet/ip address combinataion 69419859Swpaul * given. When processing a reply, we must do this so that the booting 69519859Swpaul * host (i.e. the guy running rarpd), won't try to ARP for the hardware 69619859Swpaul * address of the guy being booted (he cannot answer the ARP). 6976823Swpaul */ 698249234Smariusstatic struct sockaddr_in sin_inarp = { 699246143Sglebius sizeof(struct sockaddr_in), AF_INET, 0, 70078057Sroam {0}, 70178057Sroam {0}, 70219859Swpaul}; 703249234Smarius 704249234Smariusstatic struct sockaddr_dl sin_dl = { 70578057Sroam sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6, 70696254Sroberto 0, "" 70719859Swpaul}; 708249234Smarius 709249234Smariusstatic struct { 71019859Swpaul struct rt_msghdr rthdr; 71119859Swpaul char rtspace[512]; 71219859Swpaul} rtmsg; 71319859Swpaul 714117446Smuxstatic void 715116369Sjmgupdate_arptab(u_char *ep, in_addr_t ipaddr) 7166823Swpaul{ 717216225Sglebius struct timespec tp; 71878057Sroam int cc; 719246143Sglebius struct sockaddr_in *ar, *ar2; 72078057Sroam struct sockaddr_dl *ll, *ll2; 72178057Sroam struct rt_msghdr *rt; 72278057Sroam int xtype, xindex; 72319859Swpaul static pid_t pid; 72435003Sroberto int r; 72578057Sroam static int seq; 7266823Swpaul 72735003Sroberto r = socket(PF_ROUTE, SOCK_RAW, 0); 72878057Sroam if (r == -1) { 72978402Sroam logmsg(LOG_ERR, "raw route socket: %m"); 730238282Shrs pidfile_remove(pidfile_fh); 73135003Sroberto exit(1); 7326823Swpaul } 73335003Sroberto pid = getpid(); 7346823Swpaul 73519859Swpaul ar = &sin_inarp; 73619889Swpaul ar->sin_addr.s_addr = ipaddr; 73719859Swpaul ll = &sin_dl; 73819859Swpaul bcopy(ep, LLADDR(ll), 6); 7396823Swpaul 74019859Swpaul /* Get the type and interface index */ 74119859Swpaul rt = &rtmsg.rthdr; 74219859Swpaul bzero(rt, sizeof(rtmsg)); 74319859Swpaul rt->rtm_version = RTM_VERSION; 74419859Swpaul rt->rtm_addrs = RTA_DST; 74519859Swpaul rt->rtm_type = RTM_GET; 74619859Swpaul rt->rtm_seq = ++seq; 747246143Sglebius ar2 = (struct sockaddr_in *)rtmsg.rtspace; 74819859Swpaul bcopy(ar, ar2, sizeof(*ar)); 74919859Swpaul rt->rtm_msglen = sizeof(*rt) + sizeof(*ar); 75019859Swpaul errno = 0; 75178057Sroam if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != ESRCH)) { 75278402Sroam logmsg(LOG_ERR, "rtmsg get write: %m"); 75335003Sroberto close(r); 75419889Swpaul return; 7556823Swpaul } 75619859Swpaul do { 75719859Swpaul cc = read(r, rt, sizeof(rtmsg)); 75819859Swpaul } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid)); 75978057Sroam if (cc == -1) { 76078402Sroam logmsg(LOG_ERR, "rtmsg get read: %m"); 76135003Sroberto close(r); 76219889Swpaul return; 7636823Swpaul } 76419859Swpaul ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len); 76519859Swpaul if (ll2->sdl_family != AF_LINK) { 76619859Swpaul /* 76719859Swpaul * XXX I think this means the ip address is not on a 76819859Swpaul * directly connected network (the family is AF_INET in 76919859Swpaul * this case). 77019859Swpaul */ 771116369Sjmg logmsg(LOG_ERR, "bogus link family (%d) wrong net for %08X?\n", 77219889Swpaul ll2->sdl_family, ipaddr); 77335003Sroberto close(r); 77419859Swpaul return; 77519859Swpaul } 77619859Swpaul xtype = ll2->sdl_type; 77719859Swpaul xindex = ll2->sdl_index; 77819859Swpaul 77919859Swpaul /* Set the new arp entry */ 78019859Swpaul bzero(rt, sizeof(rtmsg)); 78119859Swpaul rt->rtm_version = RTM_VERSION; 78219859Swpaul rt->rtm_addrs = RTA_DST | RTA_GATEWAY; 78319859Swpaul rt->rtm_inits = RTV_EXPIRE; 784216225Sglebius clock_gettime(CLOCK_MONOTONIC, &tp); 785216225Sglebius rt->rtm_rmx.rmx_expire = tp.tv_sec + ARPSECS; 78619859Swpaul rt->rtm_flags = RTF_HOST | RTF_STATIC; 78719859Swpaul rt->rtm_type = RTM_ADD; 78819859Swpaul rt->rtm_seq = ++seq; 78919859Swpaul 79019859Swpaul bcopy(ar, ar2, sizeof(*ar)); 79119859Swpaul 79219859Swpaul ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2)); 79319859Swpaul bcopy(ll, ll2, sizeof(*ll)); 79419859Swpaul ll2->sdl_type = xtype; 79519859Swpaul ll2->sdl_index = xindex; 79619859Swpaul 79719859Swpaul rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2); 79819859Swpaul errno = 0; 79978057Sroam if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != EEXIST)) { 80078402Sroam logmsg(LOG_ERR, "rtmsg add write: %m"); 80135003Sroberto close(r); 80219889Swpaul return; 8036823Swpaul } 80419859Swpaul do { 80519859Swpaul cc = read(r, rt, sizeof(rtmsg)); 80619859Swpaul } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid)); 80735003Sroberto close(r); 80878057Sroam if (cc == -1) { 80978402Sroam logmsg(LOG_ERR, "rtmsg add read: %m"); 81019889Swpaul return; 81119859Swpaul } 8126823Swpaul} 8136823Swpaul 8146823Swpaul/* 8156823Swpaul * Build a reverse ARP packet and sent it out on the interface. 81619859Swpaul * 'ep' points to a valid REVARP_REQUEST. The REVARP_REPLY is built 8176823Swpaul * on top of the request, then written to the network. 8186823Swpaul * 8196823Swpaul * RFC 903 defines the ether_arp fields as follows. The following comments 8206823Swpaul * are taken (more or less) straight from this document. 8216823Swpaul * 82219859Swpaul * REVARP_REQUEST 8236823Swpaul * 8246823Swpaul * arp_sha is the hardware address of the sender of the packet. 8256823Swpaul * arp_spa is undefined. 8266823Swpaul * arp_tha is the 'target' hardware address. 8276823Swpaul * In the case where the sender wishes to determine his own 8286823Swpaul * protocol address, this, like arp_sha, will be the hardware 8296823Swpaul * address of the sender. 8306823Swpaul * arp_tpa is undefined. 8316823Swpaul * 83219859Swpaul * REVARP_REPLY 8336823Swpaul * 8346823Swpaul * arp_sha is the hardware address of the responder (the sender of the 8356823Swpaul * reply packet). 8366823Swpaul * arp_spa is the protocol address of the responder (see the note below). 8376823Swpaul * arp_tha is the hardware address of the target, and should be the same as 8386823Swpaul * that which was given in the request. 8396823Swpaul * arp_tpa is the protocol address of the target, that is, the desired address. 8408857Srgrimes * 8416823Swpaul * Note that the requirement that arp_spa be filled in with the responder's 8428857Srgrimes * protocol is purely for convenience. For instance, if a system were to use 8438857Srgrimes * both ARP and RARP, then the inclusion of the valid protocol-hardware 8448857Srgrimes * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent 8456823Swpaul * ARP request. 8466823Swpaul */ 847117446Smuxstatic void 848116369Sjmgrarp_reply(struct if_info *ii, struct ether_header *ep, in_addr_t ipaddr, 84978057Sroam u_int len) 8506823Swpaul{ 85178057Sroam u_int n; 8526823Swpaul struct ether_arp *ap = (struct ether_arp *)(ep + 1); 8536823Swpaul 8546823Swpaul update_arptab((u_char *)&ap->arp_sha, ipaddr); 8556823Swpaul 8566823Swpaul /* 8576823Swpaul * Build the rarp reply by modifying the rarp request in place. 8586823Swpaul */ 85919859Swpaul ap->arp_op = htons(REVARP_REPLY); 8606823Swpaul 86119859Swpaul#ifdef BROKEN_BPF 86219859Swpaul ep->ether_type = ETHERTYPE_REVARP; 86319859Swpaul#endif 8646823Swpaul bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6); 8656823Swpaul bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6); 8666823Swpaul bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6); 8676823Swpaul 8686823Swpaul bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4); 8696823Swpaul /* Target hardware is unchanged. */ 8706823Swpaul bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4); 8716823Swpaul 87219859Swpaul /* Zero possible garbage after packet. */ 87319859Swpaul bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)), 87419859Swpaul len - (sizeof(*ep) + sizeof(*ap))); 8756823Swpaul n = write(ii->ii_fd, (char *)ep, len); 87619859Swpaul if (n != len) 87778402Sroam logmsg(LOG_ERR, "write: only %d of %d bytes written", n, len); 87819859Swpaul if (verbose) 87978402Sroam logmsg(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname, 88019985Sfenner eatoa(ap->arp_tha), 88119889Swpaul intoa(ntohl(ipaddr))); 8826823Swpaul} 8836823Swpaul 8846823Swpaul/* 8856823Swpaul * Get the netmask of an IP address. This routine is used if 8866823Swpaul * SIOCGIFNETMASK doesn't work. 8876823Swpaul */ 888117446Smuxstatic in_addr_t 889116369Sjmgipaddrtonetmask(in_addr_t addr) 8906823Swpaul{ 891249234Smarius 89219889Swpaul addr = ntohl(addr); 8936823Swpaul if (IN_CLASSA(addr)) 89419889Swpaul return htonl(IN_CLASSA_NET); 8956823Swpaul if (IN_CLASSB(addr)) 89619889Swpaul return htonl(IN_CLASSB_NET); 8976823Swpaul if (IN_CLASSC(addr)) 89819889Swpaul return htonl(IN_CLASSC_NET); 899116369Sjmg logmsg(LOG_DEBUG, "unknown IP address class: %08X", addr); 90019889Swpaul return htonl(0xffffffff); 9016823Swpaul} 90219859Swpaul 90319859Swpaul/* 90419859Swpaul * A faster replacement for inet_ntoa(). 90519859Swpaul */ 906117446Smuxstatic char * 907116369Sjmgintoa(in_addr_t addr) 90819859Swpaul{ 90978057Sroam char *cp; 91078057Sroam u_int byte; 91178057Sroam int n; 91219859Swpaul static char buf[sizeof(".xxx.xxx.xxx.xxx")]; 91319859Swpaul 91419859Swpaul cp = &buf[sizeof buf]; 91519859Swpaul *--cp = '\0'; 91619859Swpaul 91719859Swpaul n = 4; 91819859Swpaul do { 91919859Swpaul byte = addr & 0xff; 92019859Swpaul *--cp = byte % 10 + '0'; 92119859Swpaul byte /= 10; 92219859Swpaul if (byte > 0) { 92319859Swpaul *--cp = byte % 10 + '0'; 92419859Swpaul byte /= 10; 92519859Swpaul if (byte > 0) 92619859Swpaul *--cp = byte + '0'; 92719859Swpaul } 92819859Swpaul *--cp = '.'; 92919859Swpaul addr >>= 8; 93019859Swpaul } while (--n > 0); 93119859Swpaul 93219859Swpaul return cp + 1; 93319859Swpaul} 93419859Swpaul 935117446Smuxstatic char * 93678057Sroameatoa(u_char *ea) 93719859Swpaul{ 93819859Swpaul static char buf[sizeof("xx:xx:xx:xx:xx:xx")]; 93919859Swpaul 94019859Swpaul (void)sprintf(buf, "%x:%x:%x:%x:%x:%x", 94119859Swpaul ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); 94219859Swpaul return (buf); 94319859Swpaul} 94478402Sroam 945117446Smuxstatic void 94678402Sroamlogmsg(int pri, const char *fmt, ...) 94778402Sroam{ 94878402Sroam va_list v; 94978402Sroam FILE *fp; 95078402Sroam char *newfmt; 95178402Sroam 95278402Sroam va_start(v, fmt); 95378402Sroam if (dflag) { 95478402Sroam if (pri == LOG_ERR) 95578402Sroam fp = stderr; 95678402Sroam else 95778402Sroam fp = stdout; 95878402Sroam if (expand_syslog_m(fmt, &newfmt) == -1) { 95978402Sroam vfprintf(fp, fmt, v); 96078402Sroam } else { 96178402Sroam vfprintf(fp, newfmt, v); 96278402Sroam free(newfmt); 96378402Sroam } 96478402Sroam fputs("\n", fp); 96578402Sroam fflush(fp); 96678402Sroam } else { 96778402Sroam vsyslog(pri, fmt, v); 96878402Sroam } 96978402Sroam va_end(v); 97078402Sroam} 97178402Sroam 972117446Smuxstatic int 97378402Sroamexpand_syslog_m(const char *fmt, char **newfmt) { 97478402Sroam const char *str, *m; 97578402Sroam char *p, *np; 97678402Sroam 97778402Sroam p = strdup(""); 97878402Sroam str = fmt; 97978402Sroam while ((m = strstr(str, "%m")) != NULL) { 98079333Smjacob asprintf(&np, "%s%.*s%s", p, (int)(m - str), 98179333Smjacob str, strerror(errno)); 98278402Sroam free(p); 98378402Sroam if (np == NULL) { 98478402Sroam errno = ENOMEM; 98578402Sroam return (-1); 98678402Sroam } 98778402Sroam p = np; 98878402Sroam str = m + 2; 98978402Sroam } 99078402Sroam 99178402Sroam if (*str != '\0') { 99278402Sroam asprintf(&np, "%s%s", p, str); 99378402Sroam free(p); 99478402Sroam if (np == NULL) { 99578402Sroam errno = ENOMEM; 99678402Sroam return (-1); 99778402Sroam } 99878402Sroam p = np; 99978402Sroam } 100078402Sroam 100178402Sroam *newfmt = p; 100278402Sroam return (0); 100378402Sroam} 1004