/* rarpd.c Created: Nov 12, 1992 by Philip Homburg Changed: May 13, 1995 by Kees J. Bot Rewrite to handle multiple ethernets. Changed: Jul 18, 1995 by Kees J. Bot Do RARP requests (formerly inet's job) Changed: Dec 14, 1996 by Kees J. Bot Query the netmask Changed: Dec 11, 2000 by Kees J. Bot Dressed down to be only a RARP server, giving the floor to DHCP */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_RARP_RETRIES 5 #define RARP_TIMEOUT 5 typedef struct rarp46 { ether_addr_t a46_dstaddr; ether_addr_t a46_srcaddr; ether_type_t a46_ethtype; u16_t a46_hdr, a46_pro; u8_t a46_hln, a46_pln; u16_t a46_op; ether_addr_t a46_sha; u8_t a46_spa[4]; ether_addr_t a46_tha; u8_t a46_tpa[4]; char a46_padding[ETH_MIN_PACK_SIZE - (4*6 + 2*4 + 4*2 + 2*1)]; } rarp46_t; #define ETH_RARP_PROTO 0x8035 #define RARP_ETHERNET 1 #define RARP_REQUEST 3 #define RARP_REPLY 4 static char *program; static unsigned debug; #define between(a, c, z) ((unsigned) (c) - (a) <= (unsigned) (z) - (a)) static void report(const char *label) { fprintf(stderr, "%s: %s: %s\n", program, label, strerror(errno)); } static void fatal(const char *label) { report(label); exit(1); } static void *allocate(size_t size) { void *mem; if ((mem= malloc(size)) == NULL) fatal("Can't allocate memory"); return mem; } static char *ethdev(int n) { static char an_ethdev[]= "/dev/ethNNN"; sprintf(an_ethdev + sizeof(an_ethdev)-4, "%d", n); return an_ethdev; } static char *ipdev(int n) { static char an_ipdev[]= "/dev/ipNNN"; sprintf(an_ipdev + sizeof(an_ipdev)-4, "%d", n); return an_ipdev; } typedef struct ethernet { int n; /* Network number. */ int eth_fd; /* Open low level ethernet device. */ ether_addr_t eth_addr; /* Ethernet address of this net. */ char packet[ETH_MAX_PACK_SIZE]; /* Incoming packet. */ ipaddr_t ip_addr; /* IP address of this net. */ ipaddr_t ip_mask; /* Associated netmask. */ } ethernet_t; static ethernet_t *ethernets; static void onsig(int sig) { switch (sig) { case SIGUSR1: debug++; break; case SIGUSR2: debug= 0; break; } } static void rarp_reply(ethernet_t *ep, char *hostname, ipaddr_t ip_addr, ether_addr_t eth_addr) { rarp46_t rarp46; /* Construct a RARP reply packet and send it. */ rarp46.a46_dstaddr= eth_addr; rarp46.a46_hdr= HTONS(RARP_ETHERNET); rarp46.a46_pro= HTONS(ETH_IP_PROTO); rarp46.a46_hln= 6; rarp46.a46_pln= 4; rarp46.a46_op= HTONS(RARP_REPLY); rarp46.a46_sha= ep->eth_addr; memcpy(rarp46.a46_spa, &ep->ip_addr, sizeof(ipaddr_t)); rarp46.a46_tha= eth_addr; memcpy(rarp46.a46_tpa, &ip_addr, sizeof(ipaddr_t)); if (debug >= 1) { printf("%s: Replying %s (%s) to %s\n", ethdev(ep->n), inet_ntoa(ip_addr), hostname, ether_ntoa(ð_addr)); } (void) write(ep->eth_fd, &rarp46, sizeof(rarp46)); } static int addhostname(char *addname, char *hostname, int n) { /* Create an additional hostname for a given hostname by adding "-n" to * the first part. E.g. given "wombat.cs.vu.nl" and n=2 return * "wombat-2.cs.vu.nl". This is useful for VU practical work where * people get a few extra ethernet addresses on a machine and are asked * to build a TCP/IP stack on it. */ char *dot; if (strlen(hostname) + 4 >= 1024) return 0; if ((dot= strchr(hostname, '.')) == NULL) dot= strchr(hostname, 0); sprintf(addname, "%.*s-%d%s", (dot - hostname), hostname, n, dot); return 1; } static void usage(void) { fprintf(stderr, "Usage: %s [-d[level]] network-name ...\n", program); exit(1); } static int ifname2n(const char *name) { /* Translate an interface name, ip0, ip1, etc, to a number. */ const char *np; char *end; unsigned long n; np= name; if (*np++ != 'i' || *np++ != 'p') usage(); n= strtoul(np, &end, 10); if (end == np || *end != 0) usage(); if (n >= 1000) { fprintf(stderr, "%s: Network number of \"%s\" is a bit large\n", program, name); exit(1); } return n; } int main(int argc, char **argv) { int i; ethernet_t *ep; nwio_ethopt_t ethopt; nwio_ethstat_t ethstat; char hostname[1024]; struct hostent *hostent; struct sigaction sa; nwio_ipconf_t ipconf; asynchio_t asyn; ssize_t n; ipaddr_t ip_addr; rarp46_t rarp46; int fd; int n_eths; program= argv[0]; asyn_init(&asyn); debug= 0; i= 1; while (i < argc && argv[i][0] == '-') { char *opt= argv[i++]+1; if (opt[0] == '-' && opt[1] == 0) break; /* -- */ while (*opt != 0) switch (*opt++) { case 'd': debug= 1; if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10); break; default: usage(); } } if ((n_eths= (argc - i)) == 0) usage(); #if __minix_vmd /* Minix-vmd can handle all nets at once using async I/O. */ ethernets= allocate(n_eths * sizeof(ethernets[0])); for (i= 0; i < n_eths; i++) { ethernets[i].n= ifname2n(argv[argc - n_eths + i]); } #else /* Minix forks n-1 times to handle each net in a process each. */ for (i= 0; i < n_eths; i++) { if (i+1 < n_eths) { switch (fork()) { case -1: fatal("fork()"); case 0: break; default: continue; } } ethernets= allocate(1 * sizeof(ethernets[0])); ethernets[0].n= ifname2n(argv[argc - n_eths + i]); } n_eths= 1; #endif sa.sa_handler= onsig; sigemptyset(&sa.sa_mask); sa.sa_flags= 0; sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); for (i= 0; i < n_eths; i++) { ep= ðernets[i]; if ((ep->eth_fd= open(ethdev(ep->n), O_RDWR)) < 0) fatal(ethdev(ep->n)); if (ioctl(ep->eth_fd, NWIOGETHSTAT, ðstat) < 0) { fprintf(stderr, "%s: %s: Unable to get eth statistics: %s\n", program, ethdev(ep->n), strerror(errno)); exit(1); } ep->eth_addr= ethstat.nwes_addr; if (debug >= 1) { printf("%s: Ethernet address is %s\n", ethdev(ep->n), ether_ntoa(&ep->eth_addr)); } ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD | NWEO_TYPESPEC; ethopt.nweo_type= HTONS(ETH_RARP_PROTO); if (ioctl(ep->eth_fd, NWIOSETHOPT, ðopt) < 0) { fprintf(stderr, "%s: %s: Unable to set eth options: %s\n", program, ethdev(ep->n), strerror(errno)); exit(1); } /* What are my address and netmask? */ if ((fd= open(ipdev(ep->n), O_RDWR)) < 0) fatal(ipdev(ep->n)); if (ioctl(fd, NWIOGIPCONF, &ipconf) < 0) fatal(ipdev(ep->n)); ep->ip_addr= ipconf.nwic_ipaddr; ep->ip_mask= ipconf.nwic_netmask; close(fd); if (debug >= 1) { printf("%s: IP address is %s / ", ipdev(ep->n), inet_ntoa(ep->ip_addr)); printf("%s\n", inet_ntoa(ep->ip_mask)); } } /* Wait for RARP requests, reply, repeat. */ for(;;) { fflush(NULL); /* Wait for a RARP request. */ for (i= 0; i < n_eths; i++) { ep= ðernets[i]; n= asyn_read(&asyn, ep->eth_fd, ep->packet, sizeof(ep->packet)); if (n != -1) break; if (errno != EINPROGRESS) { report(ethdev(ep->n)); sleep(10); } } /* RARP request? */ if (i < n_eths && n >= sizeof(rarp46) && (memcpy(&rarp46, ep->packet, sizeof(rarp46)), 1) && rarp46.a46_hdr == HTONS(RARP_ETHERNET) && rarp46.a46_pro == HTONS(ETH_IP_PROTO) && rarp46.a46_hln == 6 && rarp46.a46_pln == 4 && rarp46.a46_op == HTONS(RARP_REQUEST) ) { if ((ether_ntohost(hostname, &rarp46.a46_tha) == 0 || (rarp46.a46_tha.ea_addr[0] == 'v' && (memcpy(&ip_addr, rarp46.a46_tha.ea_addr+2, 4), 1) && (hostent= gethostbyaddr((char*) &ip_addr, 4, AF_INET)) != NULL && addhostname(hostname, hostent->h_name, rarp46.a46_tha.ea_addr[1]))) && (hostent= gethostbyname(hostname)) != NULL && hostent->h_addrtype == AF_INET ) { /* Host is found in the ethers file and the DNS, or the * ethernet address denotes a special additional address * used for implementing a TCP/IP stack in user space. */ for (i= 0; hostent->h_addr_list[i] != NULL; i++) { memcpy(&ip_addr, hostent->h_addr_list[i], sizeof(ipaddr_t)); /* Check if the address is on this network. */ if (((ip_addr ^ ep->ip_addr) & ep->ip_mask) == 0) break; } if (hostent->h_addr_list[i] != NULL) { rarp_reply(ep, hostname, ip_addr, rarp46.a46_tha); } else { if (debug >= 2) { printf("%s: Host '%s' (%s) is on the wrong net\n", ethdev(ep->n), hostname, ether_ntoa(&rarp46.a46_tha)); } } } else { if (debug >= 2) { printf("%s: RARP request from unknown host '%s'\n", ethdev(ep->n), ether_ntoa(&rarp46.a46_tha)); } } } /* Wait for another request. */ if (asyn_wait(&asyn, 0, NULL) < 0) { report("asyn_wait()"); sleep(10); } } }