[9] | 1 | /*
|
---|
| 2 | rarpd.c
|
---|
| 3 |
|
---|
| 4 | Created: Nov 12, 1992 by Philip Homburg
|
---|
| 5 |
|
---|
| 6 | Changed: May 13, 1995 by Kees J. Bot
|
---|
| 7 | Rewrite to handle multiple ethernets.
|
---|
| 8 |
|
---|
| 9 | Changed: Jul 18, 1995 by Kees J. Bot
|
---|
| 10 | Do RARP requests (formerly inet's job)
|
---|
| 11 |
|
---|
| 12 | Changed: Dec 14, 1996 by Kees J. Bot
|
---|
| 13 | Query the netmask
|
---|
| 14 |
|
---|
| 15 | Changed: Dec 11, 2000 by Kees J. Bot
|
---|
| 16 | Dressed down to be only a RARP server, giving the floor to DHCP
|
---|
| 17 | */
|
---|
| 18 |
|
---|
| 19 | #include <sys/types.h>
|
---|
| 20 | #include <sys/ioctl.h>
|
---|
| 21 | #include <errno.h>
|
---|
| 22 | #include <fcntl.h>
|
---|
| 23 | #include <stdio.h>
|
---|
| 24 | #include <stdlib.h>
|
---|
| 25 | #include <signal.h>
|
---|
| 26 | #include <string.h>
|
---|
| 27 | #include <unistd.h>
|
---|
| 28 | #include <sys/asynchio.h>
|
---|
| 29 | #include <net/hton.h>
|
---|
| 30 | #include <net/gen/socket.h>
|
---|
| 31 | #include <net/gen/netdb.h>
|
---|
| 32 | #include <net/gen/in.h>
|
---|
| 33 | #include <net/gen/inet.h>
|
---|
| 34 | #include <net/gen/ether.h>
|
---|
| 35 | #include <net/gen/eth_io.h>
|
---|
| 36 | #include <net/gen/if_ether.h>
|
---|
| 37 | #include <net/gen/ip_io.h>
|
---|
| 38 | #include <net/gen/nameser.h>
|
---|
| 39 |
|
---|
| 40 | #define MAX_RARP_RETRIES 5
|
---|
| 41 | #define RARP_TIMEOUT 5
|
---|
| 42 |
|
---|
| 43 | typedef struct rarp46
|
---|
| 44 | {
|
---|
| 45 | ether_addr_t a46_dstaddr;
|
---|
| 46 | ether_addr_t a46_srcaddr;
|
---|
| 47 | ether_type_t a46_ethtype;
|
---|
| 48 | u16_t a46_hdr, a46_pro;
|
---|
| 49 | u8_t a46_hln, a46_pln;
|
---|
| 50 | u16_t a46_op;
|
---|
| 51 | ether_addr_t a46_sha;
|
---|
| 52 | u8_t a46_spa[4];
|
---|
| 53 | ether_addr_t a46_tha;
|
---|
| 54 | u8_t a46_tpa[4];
|
---|
| 55 | char a46_padding[ETH_MIN_PACK_SIZE - (4*6 + 2*4 + 4*2 + 2*1)];
|
---|
| 56 | } rarp46_t;
|
---|
| 57 |
|
---|
| 58 | #define ETH_RARP_PROTO 0x8035
|
---|
| 59 |
|
---|
| 60 | #define RARP_ETHERNET 1
|
---|
| 61 |
|
---|
| 62 | #define RARP_REQUEST 3
|
---|
| 63 | #define RARP_REPLY 4
|
---|
| 64 |
|
---|
| 65 | static char *program;
|
---|
| 66 | static unsigned debug;
|
---|
| 67 |
|
---|
| 68 | #define between(a, c, z) ((unsigned) (c) - (a) <= (unsigned) (z) - (a))
|
---|
| 69 |
|
---|
| 70 | static void report(const char *label)
|
---|
| 71 | {
|
---|
| 72 | fprintf(stderr, "%s: %s: %s\n", program, label, strerror(errno));
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | static void fatal(const char *label)
|
---|
| 76 | {
|
---|
| 77 | report(label);
|
---|
| 78 | exit(1);
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | static void *allocate(size_t size)
|
---|
| 82 | {
|
---|
| 83 | void *mem;
|
---|
| 84 |
|
---|
| 85 | if ((mem= malloc(size)) == NULL) fatal("Can't allocate memory");
|
---|
| 86 | return mem;
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | static char *ethdev(int n)
|
---|
| 90 | {
|
---|
| 91 | static char an_ethdev[]= "/dev/ethNNN";
|
---|
| 92 |
|
---|
| 93 | sprintf(an_ethdev + sizeof(an_ethdev)-4, "%d", n);
|
---|
| 94 | return an_ethdev;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | static char *ipdev(int n)
|
---|
| 98 | {
|
---|
| 99 | static char an_ipdev[]= "/dev/ipNNN";
|
---|
| 100 |
|
---|
| 101 | sprintf(an_ipdev + sizeof(an_ipdev)-4, "%d", n);
|
---|
| 102 | return an_ipdev;
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | typedef struct ethernet {
|
---|
| 106 | int n; /* Network number. */
|
---|
| 107 | int eth_fd; /* Open low level ethernet device. */
|
---|
| 108 | ether_addr_t eth_addr; /* Ethernet address of this net. */
|
---|
| 109 | char packet[ETH_MAX_PACK_SIZE]; /* Incoming packet. */
|
---|
| 110 | ipaddr_t ip_addr; /* IP address of this net. */
|
---|
| 111 | ipaddr_t ip_mask; /* Associated netmask. */
|
---|
| 112 | } ethernet_t;
|
---|
| 113 |
|
---|
| 114 | static ethernet_t *ethernets;
|
---|
| 115 |
|
---|
| 116 | static void onsig(int sig)
|
---|
| 117 | {
|
---|
| 118 | switch (sig) {
|
---|
| 119 | case SIGUSR1: debug++; break;
|
---|
| 120 | case SIGUSR2: debug= 0; break;
|
---|
| 121 | }
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | static void rarp_reply(ethernet_t *ep, char *hostname, ipaddr_t ip_addr,
|
---|
| 125 | ether_addr_t eth_addr)
|
---|
| 126 | {
|
---|
| 127 | rarp46_t rarp46;
|
---|
| 128 |
|
---|
| 129 | /* Construct a RARP reply packet and send it. */
|
---|
| 130 | rarp46.a46_dstaddr= eth_addr;
|
---|
| 131 | rarp46.a46_hdr= HTONS(RARP_ETHERNET);
|
---|
| 132 | rarp46.a46_pro= HTONS(ETH_IP_PROTO);
|
---|
| 133 | rarp46.a46_hln= 6;
|
---|
| 134 | rarp46.a46_pln= 4;
|
---|
| 135 | rarp46.a46_op= HTONS(RARP_REPLY);
|
---|
| 136 | rarp46.a46_sha= ep->eth_addr;
|
---|
| 137 | memcpy(rarp46.a46_spa, &ep->ip_addr, sizeof(ipaddr_t));
|
---|
| 138 | rarp46.a46_tha= eth_addr;
|
---|
| 139 | memcpy(rarp46.a46_tpa, &ip_addr, sizeof(ipaddr_t));
|
---|
| 140 |
|
---|
| 141 | if (debug >= 1) {
|
---|
| 142 | printf("%s: Replying %s (%s) to %s\n",
|
---|
| 143 | ethdev(ep->n), inet_ntoa(ip_addr), hostname, ether_ntoa(ð_addr));
|
---|
| 144 | }
|
---|
| 145 | (void) write(ep->eth_fd, &rarp46, sizeof(rarp46));
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | static int addhostname(char *addname, char *hostname, int n)
|
---|
| 149 | {
|
---|
| 150 | /* Create an additional hostname for a given hostname by adding "-n" to
|
---|
| 151 | * the first part. E.g. given "wombat.cs.vu.nl" and n=2 return
|
---|
| 152 | * "wombat-2.cs.vu.nl". This is useful for VU practical work where
|
---|
| 153 | * people get a few extra ethernet addresses on a machine and are asked
|
---|
| 154 | * to build a TCP/IP stack on it.
|
---|
| 155 | */
|
---|
| 156 | char *dot;
|
---|
| 157 |
|
---|
| 158 | if (strlen(hostname) + 4 >= 1024) return 0;
|
---|
| 159 | if ((dot= strchr(hostname, '.')) == NULL) dot= strchr(hostname, 0);
|
---|
| 160 | sprintf(addname, "%.*s-%d%s", (dot - hostname), hostname, n, dot);
|
---|
| 161 | return 1;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | static void usage(void)
|
---|
| 165 | {
|
---|
| 166 | fprintf(stderr, "Usage: %s [-d[level]] network-name ...\n", program);
|
---|
| 167 | exit(1);
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | static int ifname2n(const char *name)
|
---|
| 171 | {
|
---|
| 172 | /* Translate an interface name, ip0, ip1, etc, to a number. */
|
---|
| 173 | const char *np;
|
---|
| 174 | char *end;
|
---|
| 175 | unsigned long n;
|
---|
| 176 |
|
---|
| 177 | np= name;
|
---|
| 178 | if (*np++ != 'i' || *np++ != 'p') usage();
|
---|
| 179 | n= strtoul(np, &end, 10);
|
---|
| 180 | if (end == np || *end != 0) usage();
|
---|
| 181 | if (n >= 1000) {
|
---|
| 182 | fprintf(stderr, "%s: Network number of \"%s\" is a bit large\n",
|
---|
| 183 | program, name);
|
---|
| 184 | exit(1);
|
---|
| 185 | }
|
---|
| 186 | return n;
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | int main(int argc, char **argv)
|
---|
| 190 | {
|
---|
| 191 | int i;
|
---|
| 192 | ethernet_t *ep;
|
---|
| 193 | nwio_ethopt_t ethopt;
|
---|
| 194 | nwio_ethstat_t ethstat;
|
---|
| 195 | char hostname[1024];
|
---|
| 196 | struct hostent *hostent;
|
---|
| 197 | struct sigaction sa;
|
---|
| 198 | nwio_ipconf_t ipconf;
|
---|
| 199 | asynchio_t asyn;
|
---|
| 200 | ssize_t n;
|
---|
| 201 | ipaddr_t ip_addr;
|
---|
| 202 | rarp46_t rarp46;
|
---|
| 203 | int fd;
|
---|
| 204 | int n_eths;
|
---|
| 205 |
|
---|
| 206 | program= argv[0];
|
---|
| 207 | asyn_init(&asyn);
|
---|
| 208 |
|
---|
| 209 | debug= 0;
|
---|
| 210 | i= 1;
|
---|
| 211 | while (i < argc && argv[i][0] == '-') {
|
---|
| 212 | char *opt= argv[i++]+1;
|
---|
| 213 |
|
---|
| 214 | if (opt[0] == '-' && opt[1] == 0) break; /* -- */
|
---|
| 215 |
|
---|
| 216 | while (*opt != 0) switch (*opt++) {
|
---|
| 217 | case 'd':
|
---|
| 218 | debug= 1;
|
---|
| 219 | if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
|
---|
| 220 | break;
|
---|
| 221 | default:
|
---|
| 222 | usage();
|
---|
| 223 | }
|
---|
| 224 | }
|
---|
| 225 |
|
---|
| 226 | if ((n_eths= (argc - i)) == 0) usage();
|
---|
| 227 |
|
---|
| 228 | #if __minix_vmd
|
---|
| 229 | /* Minix-vmd can handle all nets at once using async I/O. */
|
---|
| 230 | ethernets= allocate(n_eths * sizeof(ethernets[0]));
|
---|
| 231 | for (i= 0; i < n_eths; i++) {
|
---|
| 232 | ethernets[i].n= ifname2n(argv[argc - n_eths + i]);
|
---|
| 233 | }
|
---|
| 234 | #else
|
---|
| 235 | /* Minix forks n-1 times to handle each net in a process each. */
|
---|
| 236 | for (i= 0; i < n_eths; i++) {
|
---|
| 237 | if (i+1 < n_eths) {
|
---|
| 238 | switch (fork()) {
|
---|
| 239 | case -1: fatal("fork()");
|
---|
| 240 | case 0: break;
|
---|
| 241 | default: continue;
|
---|
| 242 | }
|
---|
| 243 | }
|
---|
| 244 | ethernets= allocate(1 * sizeof(ethernets[0]));
|
---|
| 245 | ethernets[0].n= ifname2n(argv[argc - n_eths + i]);
|
---|
| 246 | }
|
---|
| 247 | n_eths= 1;
|
---|
| 248 | #endif
|
---|
| 249 |
|
---|
| 250 | sa.sa_handler= onsig;
|
---|
| 251 | sigemptyset(&sa.sa_mask);
|
---|
| 252 | sa.sa_flags= 0;
|
---|
| 253 | sigaction(SIGUSR1, &sa, NULL);
|
---|
| 254 | sigaction(SIGUSR2, &sa, NULL);
|
---|
| 255 |
|
---|
| 256 | for (i= 0; i < n_eths; i++) {
|
---|
| 257 | ep= ðernets[i];
|
---|
| 258 | if ((ep->eth_fd= open(ethdev(ep->n), O_RDWR)) < 0) fatal(ethdev(ep->n));
|
---|
| 259 |
|
---|
| 260 | if (ioctl(ep->eth_fd, NWIOGETHSTAT, ðstat) < 0) {
|
---|
| 261 | fprintf(stderr, "%s: %s: Unable to get eth statistics: %s\n",
|
---|
| 262 | program, ethdev(ep->n), strerror(errno));
|
---|
| 263 | exit(1);
|
---|
| 264 | }
|
---|
| 265 | ep->eth_addr= ethstat.nwes_addr;
|
---|
| 266 | if (debug >= 1) {
|
---|
| 267 | printf("%s: Ethernet address is %s\n",
|
---|
| 268 | ethdev(ep->n), ether_ntoa(&ep->eth_addr));
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD |
|
---|
| 272 | NWEO_TYPESPEC;
|
---|
| 273 | ethopt.nweo_type= HTONS(ETH_RARP_PROTO);
|
---|
| 274 |
|
---|
| 275 | if (ioctl(ep->eth_fd, NWIOSETHOPT, ðopt) < 0) {
|
---|
| 276 | fprintf(stderr, "%s: %s: Unable to set eth options: %s\n",
|
---|
| 277 | program, ethdev(ep->n), strerror(errno));
|
---|
| 278 | exit(1);
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | /* What are my address and netmask? */
|
---|
| 282 | if ((fd= open(ipdev(ep->n), O_RDWR)) < 0) fatal(ipdev(ep->n));
|
---|
| 283 | if (ioctl(fd, NWIOGIPCONF, &ipconf) < 0) fatal(ipdev(ep->n));
|
---|
| 284 |
|
---|
| 285 | ep->ip_addr= ipconf.nwic_ipaddr;
|
---|
| 286 | ep->ip_mask= ipconf.nwic_netmask;
|
---|
| 287 | close(fd);
|
---|
| 288 | if (debug >= 1) {
|
---|
| 289 | printf("%s: IP address is %s / ",
|
---|
| 290 | ipdev(ep->n), inet_ntoa(ep->ip_addr));
|
---|
| 291 | printf("%s\n", inet_ntoa(ep->ip_mask));
|
---|
| 292 | }
|
---|
| 293 | }
|
---|
| 294 |
|
---|
| 295 | /* Wait for RARP requests, reply, repeat. */
|
---|
| 296 | for(;;) {
|
---|
| 297 | fflush(NULL);
|
---|
| 298 |
|
---|
| 299 | /* Wait for a RARP request. */
|
---|
| 300 | for (i= 0; i < n_eths; i++) {
|
---|
| 301 | ep= ðernets[i];
|
---|
| 302 |
|
---|
| 303 | n= asyn_read(&asyn, ep->eth_fd, ep->packet, sizeof(ep->packet));
|
---|
| 304 | if (n != -1) break;
|
---|
| 305 | if (errno != EINPROGRESS) {
|
---|
| 306 | report(ethdev(ep->n));
|
---|
| 307 | sleep(10);
|
---|
| 308 | }
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | /* RARP request? */
|
---|
| 312 | if (i < n_eths
|
---|
| 313 | && n >= sizeof(rarp46)
|
---|
| 314 | && (memcpy(&rarp46, ep->packet, sizeof(rarp46)), 1)
|
---|
| 315 | && rarp46.a46_hdr == HTONS(RARP_ETHERNET)
|
---|
| 316 | && rarp46.a46_pro == HTONS(ETH_IP_PROTO)
|
---|
| 317 | && rarp46.a46_hln == 6
|
---|
| 318 | && rarp46.a46_pln == 4
|
---|
| 319 | && rarp46.a46_op == HTONS(RARP_REQUEST)
|
---|
| 320 | ) {
|
---|
| 321 | if ((ether_ntohost(hostname, &rarp46.a46_tha) == 0
|
---|
| 322 | || (rarp46.a46_tha.ea_addr[0] == 'v'
|
---|
| 323 | && (memcpy(&ip_addr, rarp46.a46_tha.ea_addr+2, 4), 1)
|
---|
| 324 | && (hostent= gethostbyaddr((char*) &ip_addr,
|
---|
| 325 | 4, AF_INET)) != NULL
|
---|
| 326 | && addhostname(hostname, hostent->h_name,
|
---|
| 327 | rarp46.a46_tha.ea_addr[1])))
|
---|
| 328 | && (hostent= gethostbyname(hostname)) != NULL
|
---|
| 329 | && hostent->h_addrtype == AF_INET
|
---|
| 330 | ) {
|
---|
| 331 | /* Host is found in the ethers file and the DNS, or the
|
---|
| 332 | * ethernet address denotes a special additional address
|
---|
| 333 | * used for implementing a TCP/IP stack in user space.
|
---|
| 334 | */
|
---|
| 335 | for (i= 0; hostent->h_addr_list[i] != NULL; i++) {
|
---|
| 336 | memcpy(&ip_addr, hostent->h_addr_list[i], sizeof(ipaddr_t));
|
---|
| 337 |
|
---|
| 338 | /* Check if the address is on this network. */
|
---|
| 339 | if (((ip_addr ^ ep->ip_addr) & ep->ip_mask) == 0) break;
|
---|
| 340 | }
|
---|
| 341 |
|
---|
| 342 | if (hostent->h_addr_list[i] != NULL) {
|
---|
| 343 | rarp_reply(ep, hostname, ip_addr, rarp46.a46_tha);
|
---|
| 344 | } else {
|
---|
| 345 | if (debug >= 2) {
|
---|
| 346 | printf("%s: Host '%s' (%s) is on the wrong net\n",
|
---|
| 347 | ethdev(ep->n),
|
---|
| 348 | hostname, ether_ntoa(&rarp46.a46_tha));
|
---|
| 349 | }
|
---|
| 350 | }
|
---|
| 351 | } else {
|
---|
| 352 | if (debug >= 2) {
|
---|
| 353 | printf("%s: RARP request from unknown host '%s'\n",
|
---|
| 354 | ethdev(ep->n), ether_ntoa(&rarp46.a46_tha));
|
---|
| 355 | }
|
---|
| 356 | }
|
---|
| 357 | }
|
---|
| 358 |
|
---|
| 359 | /* Wait for another request. */
|
---|
| 360 | if (asyn_wait(&asyn, 0, NULL) < 0) {
|
---|
| 361 | report("asyn_wait()");
|
---|
| 362 | sleep(10);
|
---|
| 363 | }
|
---|
| 364 | }
|
---|
| 365 | }
|
---|