#ifndef lint static char sccsid[] = "@(#)bootp.c 1.1 (Stanford) 1/22/86"; #endif /* * BOOTP (bootstrap protocol) server daemon. * * Answers BOOTP request packets from booting client machines. * See [SRI-NIC]RFC951.TXT for a description of the protocol. */ #include #include #include #include #include #include #include #define iaddr_t struct in_addr #include #include #include #include #include #include #include #include #include #include #include #include #include #include static cap_value_t cap_network_mgt[] = {CAP_NETWORK_MGT}; char reg_netmask[30], *reg_net; char reg_hostnet[30]; int debug; int s; /* socket fd */ u_char buf[1024]; /* receive packet buffer */ long tloc; /* time of day */ #define IFMAX 200 /* maximum number of interfaces supported */ struct ifreq ifreq[IFMAX]; /* holds interface configuration */ struct ifconf ifconf; /* int. config. ioctl block (points to ifreq) */ struct arpreq arpreq; /* arp request ioctl block */ struct sockaddr_in sin; #define SINPTR(p) ((struct sockaddr_in *)(p)) /* * Globals below are associated with the bootp database file (bootptab). */ char *bootptab = "/etc/bootptab"; FILE *fp; char line[256]; /* line buffer for reading bootptab */ char *linep; /* pointer to 'line' */ int linenum; /* current line number in bootptab */ char homedir[64]; /* bootfile homedirectory */ char defaultboot[64]; /* default file to boot */ #define MHOSTS 512 /* max number of 'hosts' structs */ struct hosts { char host[MAXHOSTNAMELEN+1]; /* host name (and suffix) */ u_char htype; /* hardware type */ u_char haddr[6]; /* hardware address */ iaddr_t iaddr; /* internet address */ char bootfile[128]; /* default boot file name */ } hosts[MHOSTS]; int nhosts; /* current number of hosts */ long modtime; /* last modification time of bootptab */ int max_addr_alloc = 64; int numaddrs; u_int *myaddrs; /* save addresses of executing host */ char myname[MAXHOSTNAMELEN+1]; /* my primary name */ iaddr_t myhostaddr; /* save (main) internet address of executing host */ int forwarding; /* flag that controls cross network forwarding */ /* * List of netmasks and corresponding network numbers for all * attached interfaces that are configured for Internet. */ struct netaddr { u_long netmask; /* network mask (not shifted) */ u_long net; /* address masked by netmask */ iaddr_t myaddr; /* this hosts full ip address on that net */ } nets[IFMAX]; int bestaddr(struct in_addr *, struct in_addr *); int handle_diskless(struct bootp *, struct bootp *); int matchhost(char *); int reverse_arp(u_char *, struct in_addr *); int samewire(struct in_addr *, struct in_addr *); static char *iaddr_ntoa(iaddr_t addr); void forward(struct bootp *, struct in_addr *, struct in_addr *); void getfield(char *, int); void makenamelist(void); void readtab(void); void reply(void); void request(void); void sendreply(struct bootp *, int, int); void setarp(struct in_addr *, u_char *, int); void timeout(int); void get_aliases(char *); /* * For sgi, bootp is a single threaded server invoked * by the inet master daemon (/usr/etc/inetd). Once * a bootp has been started, it will handle all subsequent * bootp requests until it has been idle for TIMEOUT * seconds, at which point the bootp process terminates * and inetd will start listening to the IPPORT_BOOTPS * socket again. */ #define TIMEOUT 300 /* number of idle seconds to wait */ int timed_out = 0; /* * SIGALRM trap handler */ /*ARGSUSED*/ void timeout(int sig) { /* set flag for mainline */ timed_out = 1; } main(int argc, char *argv[]) { register struct bootp *bp; register int n; struct sockaddr_in from; int fromlen; register struct ifreq *ifrp; register struct netaddr *np; struct ifreq ifnetmask; int ifcount; int len; for (argc--, argv++ ; argc > 0 ; argc--, argv++) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'd': debug++; break; case 'f': /* enable cross network forwarding */ forwarding++; break; } } } time(&tloc); openlog("bootp", LOG_PID|LOG_CONS, LOG_DAEMON); if (debug) syslog(LOG_DEBUG, "starting at %s", ctime(&tloc)); /* * It is not necessary to create a socket for input. * The inet master daemon passes the required socket * to this process as fd 0. Bootp takes control of * the socket for a while and gives it back if it ever * remains idle for the timeout limit. */ s = 0; ifconf.ifc_len = sizeof ifreq; ifconf.ifc_req = ifreq; if (ioctl(s, SIOCGIFCONF, (caddr_t)&ifconf) < 0 || ifconf.ifc_len <= 0) { syslog(LOG_ERR, "'get interface config' ioctl failed (%m)"); exit(1); } /* * Save the name and IP address of the executing host */ makenamelist(); /* * Count the number of interfaces that are ifconfiged for Internet, * not counting the loopback interface. It turns out that the * forwarding code in the ip layer in the kernel is only enabled * when there are at least two interfaces that speak Internet * protocols. The BOOTP forwarding logic for cross-gateway * booting must only be invoked when the BOOTP server is running * on a system that can forward at the ip layer. * * While counting, also get the netmasks and the corresponding * network numbers. */ bzero(nets, sizeof nets); np = nets; ifcount = 0; len = ifconf.ifc_len; for (ifrp = &ifreq[0]; len > 0; len -= sizeof (struct ifreq), ifrp++) { if (SINPTR(&ifrp->ifr_addr)->sin_family == AF_INET) { strncpy(ifnetmask.ifr_name, ifrp->ifr_name, sizeof(ifnetmask.ifr_name)); if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifnetmask) < 0) { syslog(LOG_ERR, "'get interface flags' ioctl failed (%m)"); exit(1); } if ((ifnetmask.ifr_flags & (IFF_UP|IFF_LOOPBACK)) != IFF_UP) continue; if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifnetmask) < 0) { syslog(LOG_ERR, "'get interface netmask' ioctl failed (%m)"); exit(1); } np->netmask = SINPTR(&ifnetmask.ifr_addr)->sin_addr.s_addr; np->myaddr = SINPTR(&ifrp->ifr_addr)->sin_addr; np->net = np->netmask & np->myaddr.s_addr; /* This call will add all the addresses associated * with a given interfaceto the list of my * addresses, including all the aliases */ if (numaddrs >= max_addr_alloc) { max_addr_alloc *= 2; myaddrs = (u_int *)realloc((u_int *)myaddrs, max_addr_alloc*sizeof(u_int)); } myaddrs[numaddrs++] = np->myaddr.s_addr; get_aliases(ifrp->ifr_name); np++; ifcount++; } } if (ifcount < 2 && forwarding) { if (debug) syslog(LOG_DEBUG, "less than two interfaces, -f flag ignored"); forwarding = 0; } /* only log if forwarding state is potentially "unusual" for configuration */ if(debug && (ifcount>1 ^ forwarding)) syslog(LOG_DEBUG, "%d network interface(s), forwarding is %s", ifcount, forwarding ? "ENABLED" : "DISABLED"); len = sizeof (sin); if (getsockname(s, &sin, &len) < 0) { syslog(LOG_ERR, "getsockname failed (%m)"); exit(1); } /* * Set an alarm to wake up bootp after timeout */ (void) signal(SIGALRM, timeout); (void) alarm(TIMEOUT); for (;;) { if (timed_out) { if (debug) syslog(LOG_DEBUG, "timed out"); exit(0); } fromlen = sizeof (from); n = recvfrom(s, buf, sizeof buf, 0, (caddr_t)&from, &fromlen); if (n <= 0) continue; /* reset the timeout clock */ (void) alarm(TIMEOUT); bp = (struct bootp *)buf; if (n < sizeof *bp) continue; readtab(); /* (re)read bootptab */ switch (bp->bp_op) { case BOOTREQUEST: if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifnetmask) < 0) { syslog(LOG_ERR, "netmask ioctl failed (%m)"); break; } reg_net = inet_ntoa(SINPTR(&ifnetmask.ifr_addr)->sin_addr); strcpy(reg_netmask, reg_net); if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifnetmask) < 0) { syslog(LOG_ERR, "netaddr ioctl failed (%m)"); break; } reg_net = inet_ntoa(SINPTR(&ifnetmask.ifr_addr)->sin_addr); strcpy(reg_hostnet, reg_net); request(); break; case BOOTREPLY: reply(); break; } } } /* * Process BOOTREQUEST packet. * * * * Our version does implement the hostname processing * specified in RFC 951. If the client specifies a hostname, then * only that hostname will respond. We do one additional thing * that the RFC does not specify: if the server name is not specified, * then we fill it in, so that the client knows the name as well * as the IP address of the server who responded. * * Our version also implements the forwarding algorithm specified * in RFC 951, but not used in the Stanford version of this code. * If a request is received that specifies a host other than this * one, check the network address of that host and forward the * request to him if he lives on a different wire than the one * from which the request was received. * * Another change from the RFC and the original version of the * code is that the BOOTP server will respond to the client even * if the client is not listed in the BOOTP configuration file, * provided that the client already knows his IP address. The * reason for this change is to supply a bit more ease of use. * If there is a file out there that you want to boot, it seems * a shame to have to edit bootptab on the server before * you can boot the lousy file. All the more so because if you * have already configured the IP address of your system, then * the bootptab information is redundant. (This will make the * transition from XNS network boot to IP network boot a little * less painful). * * */ void request() { register struct bootp *rq = (struct bootp *)buf; struct bootp rp; char path[MAXPATHLEN], file[128]; iaddr_t fromaddr; register struct hosts *hp; register struct hostent *hostentp; register n; int has_sname; /* does rq have nonempty bp_sname? */ /* prevent possible buffer overrun */ rq->bp_file[sizeof(rq->bp_file)-1] = '\0'; rp = *rq; /* copy request into reply */ rp.bp_op = BOOTREPLY; /* * Let's resolve client's ipaddr first. */ if (rq->bp_yiaddr.s_addr != 0 && rq->bp_giaddr.s_addr != 0) { /* * yiaddr has already been filled in by forwarding bootp */ hp = (struct hosts *) 0; } else if (rq->bp_ciaddr.s_addr == 0) { /* * client doesnt know his IP address, * search by hardware address. */ for (hp = &hosts[0], n = 0 ; n < nhosts ; n++,hp++) if (rq->bp_htype == hp->htype && bcmp(rq->bp_chaddr, hp->haddr, 6) == 0) break; if (n == nhosts) { /* * The requestor isn't listed in bootptab. */ hp = (struct hosts *) 0; if( (*(int *)rp.vd_magic == VM_SGI) && (rp.vd_flags == VF_GET_IPADDR) ) { if ((hostentp = gethostbyname((const char *)rp.vd_clntname)) == 0) { rp.bp_yiaddr.s_addr = 0; } else { rp.bp_yiaddr.s_addr = *(u_long *)(hostentp->h_addr_list[0]); rp.vd_flags = VF_RET_IPADDR; } } /* * Try Reverse ARP using /etc/ethers or NIS before * giving up. */ else if (!reverse_arp(rq->bp_chaddr, &rp.bp_yiaddr)) { /* * Don't trash the request at this point, * since it may be for another server with * better tables than we have. */ if (debug) syslog(LOG_DEBUG, "no IP address for %s found in /etc/ethers or ethers map, can't reply", ether_ntoa(rq->bp_chaddr)); /* Play it safe */ rp.bp_yiaddr.s_addr = 0; } } else { rp.bp_yiaddr = hp->iaddr; } } else { /* search by IP address */ for (hp = &hosts[0], n = 0 ; n < nhosts ; n++,hp++) if (rq->bp_ciaddr.s_addr == hp->iaddr.s_addr) break; if (n == nhosts) { /* * The requestor knows his Internet address already, * but he isn't listed in bootptab. Try to satisfy * the request anyway. */ hp = (struct hosts *) 0; } } fromaddr = rq->bp_ciaddr.s_addr ? rq->bp_ciaddr : rp.bp_yiaddr; /* * Check whether the requestor specified a particular server. * If not, fill in the name of the current host. If a * particular server was requested, then don't answer the * request unless the name matches our name or one of our * aliases. */ has_sname = rq->bp_sname[0] != '\0'; if (!has_sname) strncpy((char *)rp.bp_sname, myname, sizeof(rp.bp_sname)); else if (!matchhost((char *)rq->bp_sname)) { iaddr_t destaddr; if (debug) syslog(LOG_DEBUG, "%s: request for server %s", iaddr_ntoa(fromaddr), rq->bp_sname); /* * Not for us. */ if (!forwarding) return; /* * Look up the host by name and decide whether * we should forward the message to him. */ if ((hostentp = gethostbyname((const char *)rq->bp_sname)) == 0) { syslog(LOG_INFO, "%s: request for unknown server %s", iaddr_ntoa(fromaddr), rq->bp_sname); return; } destaddr.s_addr = *(u_long *)(hostentp->h_addr_list[0]); /* * If the other server is on a different cable from the * requestor, then forward the request. If on the same * wire, there is no point in forwarding. Note that in * the case that we don't yet know the IP address of the * client, there is no way to tell whether the client and * server are actually on the same wire. In that case * we forward regardless. It's redundant, but there's * no way to get around it. */ if (!samewire(&fromaddr, &destaddr)) { /* * If we were able to compute the client's Internet * address, pass that information along in case the * other server doesn't have the info. */ rq->bp_yiaddr = rp.bp_yiaddr; forward(rq, &fromaddr, &destaddr); } return; } if (fromaddr.s_addr == 0 && *(int *)(rq->vd_magic) == VM_AUTOREG && strlen((const char *)rq->vd_clntname)) { if (debug) syslog(LOG_DEBUG, "Autoreg starts"); if (handle_diskless(rq, &rp)) return; fromaddr = rp.bp_yiaddr; } /* * If we get here and the 'from' address is still zero, that * means we don't recognize the client and we can't pass the buck. * So we have to give up. */ if (fromaddr.s_addr == 0) return; if (rq->bp_file[0] == 0) { /* client didnt specify file */ if (hp == (struct hosts *)0 || hp->bootfile[0] == 0) strcpy(file, defaultboot); else strcpy(file, hp->bootfile); } else { /* client did specify file */ strcpy(file, (const char *)rq->bp_file); } if (file[0] == '/') /* if absolute pathname */ strcpy(path, file); else { /* else look in boot directory */ strcpy(path, homedir); strcat(path, "/"); strcat(path, file); } /* try first to find the file with a ".host" suffix */ n = strlen(path); if (hp != (struct hosts *)0 && hp->host[0] != 0) { strcat(path, "."); strcat(path, hp->host); } if (access(path, R_OK) < 0) { path[n] = 0; /* try it without the suffix */ if (access(path, R_OK) < 0) { /* * We don't have the file. Don't respond unless * the client asked for us by name, in case some * other server does have the file. If he asked * for us by name, send him back a null pathname * so that he knows we don't have his boot file. */ if (rq->bp_sname[0] == 0) return; /* didnt ask for us */ syslog(LOG_ERR, "%s requested boot file %s: access failed (%m)", iaddr_ntoa(fromaddr), path); path[0] = '\0'; } } if (path[0] != '\0') { if (strlen(path) > sizeof(rp.bp_file)-1) { syslog(LOG_ERR, "%s: reply boot file name %s too long", iaddr_ntoa(fromaddr), path); path[0] = '\0'; } else syslog(LOG_INFO, "reply to %s: boot file %s", iaddr_ntoa(fromaddr), path); } strcpy((char *)rp.bp_file, path); sendreply(&rp, 0, has_sname); } /* * Process BOOTREPLY packet (something is using us as a gateway). */ void reply() { struct bootp *bp = (struct bootp *)buf; iaddr_t dst, gate; dst = bp->bp_yiaddr.s_addr ? bp->bp_yiaddr : bp->bp_ciaddr; if (debug) syslog(LOG_DEBUG, "forwarding BOOTP reply from %s to %s", bp->bp_sname, iaddr_ntoa(dst)); /* * Try to compute a better giaddr in case we didn't know the * client's IP address when we forwarded the original request. * The remote server would not be returning the request unless * it contained the client's Internet address, so this time * we should get the right answer. */ if (bestaddr(&dst, &gate)) bp->bp_giaddr = gate; else syslog(LOG_ERR, "reply: can't find net for %s", iaddr_ntoa(dst)); sendreply(bp, 1, 0); } /* * Forward a BOOTREQUEST packet to another server. * * RFC 951 (7.3) implies no-forward in case * bp_ciaddr = 0. */ void forward(struct bootp *bp, iaddr_t *from, iaddr_t *dest) { struct sockaddr_in to; /* * If hop >= 3, just discard(RFC951 says so). */ if (bp->bp_hops >= 3) return; bp->bp_hops++; /* * If giaddr is 0, then I am the immediate gateway. * So, set myaddr for successing forwarders to send * reply to me directly. */ if (!bp->bp_giaddr.s_addr) { (void) bestaddr(from, &bp->bp_giaddr); } to.sin_family = AF_INET; to.sin_port = htons(IPPORT_BOOTPS); to.sin_addr.s_addr = dest->s_addr; /* already in network order */ if (debug) syslog(LOG_DEBUG, "forwarding BOOTP request to %s(%s)", bp->bp_sname, iaddr_ntoa(*dest)); if (sendto(s, (caddr_t)bp, sizeof *bp, 0, &to, sizeof to) < 0) syslog(LOG_ERR, "forwarding to %s failed (%m)", iaddr_ntoa(to.sin_addr)); } /* * Send a reply packet to the client. 'forward' flag is set if we are * not the originator of this reply packet. */ void sendreply(struct bootp *bp, int forward, int has_sname) { iaddr_t dst; struct sockaddr_in to; to = sin; to.sin_port = htons(IPPORT_BOOTPC); /* * If the client IP address is specified, use that * else if gateway IP address is specified, use that * else make a temporary arp cache entry for the client's NEW * IP/hardware address and use that. */ if (bp->bp_ciaddr.s_addr) { dst = bp->bp_ciaddr; if (debug) syslog(LOG_DEBUG, "reply to client (ciaddr) %s", inet_ntoa(dst)); } else if (bp->bp_giaddr.s_addr && forward == 0) { dst = bp->bp_giaddr; to.sin_port = htons(IPPORT_BOOTPS); if (debug) syslog(LOG_DEBUG, "reply via gateway (giaddr) %s", inet_ntoa(dst)); } else { dst = bp->bp_yiaddr; if (debug) syslog(LOG_DEBUG, "reply that IP address (yiaddr) is %s", inet_ntoa(dst)); setarp(&dst, bp->bp_chaddr, bp->bp_hlen); } if (forward == 0) { /* * If we are originating this reply, we * need to find our own interface address to * put in the bp_siaddr field of the reply. * If this server is multi-homed, pick the * 'best' interface (the one on the same net * as the client). */ int gotmatch = 0; gotmatch = bestaddr(&dst, &bp->bp_siaddr); if (bp->bp_giaddr.s_addr == 0) { if (gotmatch == 0) { syslog(LOG_ERR, "can't reply to %s (unknown net)", inet_ntoa(dst)); return; } bp->bp_giaddr.s_addr = bp->bp_siaddr.s_addr; } else if (!gotmatch && has_sname) { struct hostent *hp; /* * Use the address corresponding to the name * specified by the client. */ if ((hp = gethostbyname((const char *)bp->bp_sname))) { bp->bp_siaddr = *(struct in_addr *)hp->h_addr; } } } to.sin_addr = dst; if (sendto(s, (caddr_t)bp, sizeof *bp, 0, &to, sizeof to) < 0) syslog(LOG_ERR, "send to %s failed (%m)", iaddr_ntoa(to.sin_addr)); } /* * Setup the arp cache so that IP address 'ia' will be temporarily * bound to hardware address 'ha' of length 'len'. */ void setarp(iaddr_t *ia, u_char *ha, int len) { struct sockaddr_in *si; cap_t ocap; arpreq.arp_pa.sa_family = AF_INET; si = (struct sockaddr_in *)&arpreq.arp_pa; si->sin_addr = *ia; bcopy(ha, arpreq.arp_ha.sa_data, len); ocap = cap_acquire(1, cap_network_mgt); if (ioctl(s, SIOCSARP, (caddr_t)&arpreq) < 0) syslog(LOG_ERR, "set arp ioctl failed (%m)"); cap_surrender(ocap); } /* * Read bootptab database file. Avoid rereading the file if the * write date hasnt changed since the last time we read it. */ void readtab() { struct stat st; register char *cp; int v; register i; char temp[64], tempcpy[64]; register struct hosts *hp; int skiptopercent; if (fp == 0) { if ((fp = fopen(bootptab, "r")) == NULL) { syslog(LOG_ERR, "can't open bootptab %s (%m)", bootptab); exit(1); } } fstat(fileno(fp), &st); if (st.st_mtime == modtime && st.st_nlink) return; /* hasnt been modified or deleted yet */ fclose(fp); if ((fp = fopen(bootptab, "r")) == NULL) { syslog(LOG_ERR, "can't reopen bootptab %s (%m)", bootptab); exit(1); } fstat(fileno(fp), &st); if (debug) syslog(LOG_DEBUG, "(re)reading %s", bootptab); modtime = st.st_mtime; homedir[0] = defaultboot[0] = 0; nhosts = 0; hp = &hosts[0]; linenum = 0; skiptopercent = 1; /* * read and parse each line in the file. */ for (;;) { if (fgets(line, sizeof line, fp) == NULL) break; /* done */ if ((i = strlen(line))) line[i-1] = 0; /* remove trailing newline */ linep = line; linenum++; if (line[0] == '#' || line[0] == 0 || line[0] == ' ') continue; /* skip comment lines */ /* fill in fixed leading fields */ if (homedir[0] == 0) { getfield(homedir, sizeof homedir); continue; } if (defaultboot[0] == 0) { getfield(defaultboot, sizeof defaultboot); continue; } if (skiptopercent) { /* allow for future leading fields */ if (line[0] != '%') continue; skiptopercent = 0; continue; } /* fill in host table */ getfield(hp->host, sizeof hp->host); getfield(temp, sizeof temp); sscanf(temp, "%d", &v); hp->htype = v; getfield(temp, sizeof temp); strcpy(tempcpy, temp); cp = tempcpy; /* parse hardware address */ for (i = 0 ; i < sizeof hp->haddr ; i++) { char *cpold; char c; cpold = cp; while (*cp != '.' && *cp != ':' && *cp != 0) cp++; c = *cp; /* save original terminator */ *cp = 0; cp++; if (sscanf(cpold, "%x", &v) != 1) goto badhex; hp->haddr[i] = v; if (c == 0) break; } if (hp->htype == 1 && i != 5) { badhex: syslog(LOG_ERR, "bad hex address: %s at line %d of bootptab", temp, linenum); continue; } getfield(temp, sizeof temp); i = inet_addr(temp); if (i == -1) { register struct hostent *hep; hep = gethostbyname(temp); if (hep != 0 && hep->h_addrtype == AF_INET) i = *(int *)(hep->h_addr_list[0]); } if (i == -1 || i == 0) { syslog(LOG_ERR, "bad internet address: %s at line %d of bootptab", temp, linenum); continue; } hp->iaddr.s_addr = i; getfield(hp->bootfile, sizeof hp->bootfile); if (++nhosts >= MHOSTS) { syslog(LOG_ERR, "bootptab exceeds max. number of hosts (%d)", MHOSTS); exit(1); } hp++; } } /* * Get next field from 'line' buffer into 'str'. 'linep' is the * pointer to current position. */ void getfield(char *str, int len) { register char *cp = str; for ( ; *linep && (*linep == ' ' || *linep == '\t') ; linep++) ; /* skip spaces/tabs */ if (*linep == 0) { *cp = 0; return; } len--; /* save a spot for a null */ for ( ; *linep && *linep != ' ' && *linep != '\t' ; linep++) { *cp++ = *linep; if (--len <= 0) { *cp = 0; syslog(LOG_ERR, "string truncated: %s, on line %d of bootptab", str, linenum); return; } } *cp = 0; } /* * Perform Reverse ARP lookup using /etc/ether and /etc/hosts (or * their NIS equivalents) * * eap: Ethernet address to lookup * iap: filled in if successful * Returns 1 on success, 0 on failure. */ int reverse_arp(u_char *eap, iaddr_t *iap) { register struct hostent *hp; char host[512]; /* * Call routine to access /etc/ethers or its NIS equivalent */ if (ether_ntohost(host, eap)) return (0); /* * Now access the hostname database. */ if ((hp = gethostbyname(host)) == 0) { syslog(LOG_ERR, "gethostbyname(%s) failed: %s", host, hstrerror(h_errno)); return (0); } /* * Return primary Internet address */ iap->s_addr = *(u_long *)(hp->h_addr_list[0]); return (1); } /* * Build a list of all the names and aliases by which * the current host is known on all of its interfaces. */ void makenamelist() { register struct hostent *hp; register char **namepp; char name[64]; struct ifreq *ifrp; int len; numaddrs = 0; myaddrs = (u_int *)malloc(max_addr_alloc*sizeof(u_int)); /* * Get name of host as told to the kernel and look that * up in the hostname database. */ gethostname(name, sizeof(name)); if ((hp = gethostbyname(name)) == 0) { syslog(LOG_ERR, "gethostbyname(%s) failed: %s", name, hstrerror(h_errno)); exit(1); } strcpy(myname, name); /* * Remember primary Internet address */ myhostaddr.s_addr = *(u_long *)(hp->h_addr_list[0]); } /* * Check the passed name against the current host's names. * * Return value * TRUE if match * FALSE if no match */ int matchhost(char *name) { register struct hostent *hp; int i; u_int requested_addr; if (hp = gethostbyname(name)) { requested_addr = *(u_long *)(hp->h_addr_list[0]); for (i = 0; i < numaddrs; i++) if (requested_addr == myaddrs[i]) return 1; } return 0; } /* * Select the address of the interface on this server that is * on the same net as the 'from' address. * * iaddr_t *answ: filled in always * Return value * TRUE if match * FALSE if no match */ bestaddr(iaddr_t *from, iaddr_t *answ) { register struct netaddr *np; int match = 0; if (from->s_addr == 0) { answ->s_addr = myhostaddr.s_addr; } else { /* * Search the table of nets to which the server is connected * for the net containing the source address. */ for (np = nets; np->netmask != 0; np++) if ((from->s_addr & np->netmask) == np->net) { answ->s_addr = np->myaddr.s_addr; match = 1; break; } /* * If no match in table, default to our 'primary' address */ if (np->netmask == 0) answ->s_addr = myhostaddr.s_addr; } return match; } /* * Check whether two passed IP addresses are on the same wire. * The first argument is the IP address of the requestor, so * it must be on one of the wires to which the server is * attached. * * Return value * TRUE if on same wire * FALSE if not on same wire */ int samewire(iaddr_t *src, iaddr_t *dest) { register struct netaddr *np; /* * It may well happen that src is zero, since the datagram * comes from a PROM that may not yet know its own IP address. * In that case the socket layer doesnt know what to fill in * as the from address. In that case, we have to assume that * the source and dest are on different wires. This means * we will be doing forwarding sometimes when it isnt really * necessary. */ if (src->s_addr == 0 || dest->s_addr == 0) return 0; /* * In order to take subnetworking into account, one must * use the netmask to tell how much of the IP address * actually corresponds to the real network number. * * Search the table of nets to which the server is connected * for the net containing the source address. */ for (np = nets; np->netmask != 0; np++) if ((src->s_addr & np->netmask) == np->net) return ((dest->s_addr & np->netmask) == np->net); syslog(LOG_ERR, "can't find source net for %s", inet_ntoa(*src)); return 0; } char hostmap[] = "hosts.byname"; /* * A new diskless workstation/autoreg is asking for * initial boot/IPADDR: NOTE: we never actually implemented * any of this in our PROMs. someday we should rip it all out. */ int handle_diskless(struct bootp *rq, struct bootp *rp) { char *t_host, *domain, *ypmaster, *str_match, *t_domain; char hostname[256], alias[256]; int err = 0; if (yp_get_default_domain(&domain)) { syslog(LOG_ERR, "Autoreg: can't get domain"); return(-1); } t_host = (char *)rq->vd_clntname; if (debug) syslog(LOG_DEBUG, "Autoreg: domain=%s, host=%s", domain, t_host); gethostname(hostname, sizeof(hostname)); if (gethostbyname(t_host) != NULL) { /* play safe */ rp->vd_flags = VF_RET_IPADDR; return(0); } if (!reg_netmask) { if (str_match = strrchr(reg_hostnet, '.')) *str_match = 0; } /* get ypmaster ipaddr */ if (yp_master(domain, hostmap, &ypmaster)) { if (debug) syslog(LOG_DEBUG, "no \"%s\" YPmaster found for \"%s\" domain", hostmap, domain); return(-1); } /* check and prevent broadcast storm */ if (!matchhost(ypmaster)) { if (!forwarding) { if (debug) syslog(LOG_DEBUG, "Autoreg: I am neither YPMASTER nor GATEWAY"); return(-1); } if (debug) syslog(LOG_DEBUG, "Autoreg: I am GATEWAY"); /* XXX * finding out if the requestor and YPMASTER are * at same sub-net is impossible. So let's * allow gateway issue an extra broadcast msg. */ } else if (debug) syslog(LOG_DEBUG, "Autoreg: I am YPMASTER"); /* check/create full host name */ if ((t_domain = strchr((const char *)rq->vd_clntname, '.')) != NULL) { /* if it not for the domain i belong, then ignore */ if (strcmp(domain, t_domain+1)) { if (debug) syslog(LOG_DEBUG, "Autoreg: req_domain(%s)<>domain(%s)", t_domain+1, domain); return(-1); } strcpy(hostname, t_host); *t_domain = 0; strcpy(alias, t_host); *t_domain = '.'; } else { strcpy(hostname, t_host); strcat(hostname, "."); strcat(hostname, domain); strcpy(alias, t_host); } /* can't create, other bootp may be creating at the same time or, there is no NIS */ err = registerinethost(hostname,reg_hostnet,reg_netmask,0,alias); if ((u_long)err <= YPERR_BUSY) { syslog(LOG_ERR, "Autoreg: REGISTER failed(0x%x)", err); return(-1); } rp->vd_flags = VF_NEW_IPADDR; rp->bp_yiaddr.s_addr = (u_long)(err); if (debug) syslog(LOG_DEBUG, "Autoreg: %s REGISTERED as %s", hostname, inet_ntoa(*(struct in_addr *)&err)); return(0); } static char * iaddr_ntoa(iaddr_t addr) { struct hostent *hp; if (hp = gethostbyaddr(&addr, sizeof(addr), AF_INET)) { return hp->h_name; } else { return inet_ntoa(addr); } } void get_aliases(char *ifn) { struct ifaliasreq ifalias; struct sockaddr_in *sin; bzero (&ifalias, sizeof(struct ifaliasreq)); strncpy(ifalias.ifra_name, ifn, sizeof ifalias.ifra_name); ifalias.ifra_addr.sa_family = AF_INET; /* * A cookie of zero means to return the primary address first * and subsequent calls will return the aliases as the cookie * value increase linearly. When a -1 is returned we've exhausted * the alias'es for this interface. */ ifalias.cookie = 1; while (1){ if ((ioctl(s, SIOCLIFADDR, (caddr_t)&ifalias)) < 0) { syslog(LOG_ERR, "'get alias addr' ioctl failed (%m)"); break; } if (ifalias.cookie < 0) { break; } if (numaddrs >= max_addr_alloc) { max_addr_alloc *= 2; myaddrs = (u_int *)realloc((u_int *)myaddrs, max_addr_alloc*sizeof(u_int)); } sin = (struct sockaddr_in *)&ifalias.ifra_addr; myaddrs[numaddrs++] = sin->sin_addr.s_addr; } }