]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
a2c93a76 A |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
b7080c8e A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
a2c93a76 A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
b7080c8e A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * Copyright (c) 1990 The Regents of the University of California. | |
26 | * All rights reserved. | |
27 | * | |
28 | * Redistribution and use in source and binary forms, with or without | |
29 | * modification, are permitted provided that: (1) source code distributions | |
30 | * retain the above copyright notice and this paragraph in its entirety, (2) | |
31 | * distributions including binary code include the above copyright notice and | |
32 | * this paragraph in its entirety in the documentation or other materials | |
33 | * provided with the distribution, and (3) all advertising materials mentioning | |
34 | * features or use of this software display the following acknowledgement: | |
35 | * ``This product includes software developed by the University of California, | |
36 | * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of | |
37 | * the University nor the names of its contributors may be used to endorse | |
38 | * or promote products derived from this software without specific prior | |
39 | * written permission. | |
40 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED | |
41 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | |
42 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
43 | */ | |
b8dff150 | 44 | #include <sys/cdefs.h> |
b7080c8e | 45 | #ifndef lint |
b8dff150 | 46 | __unused char copyright[] = |
b7080c8e A |
47 | "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ |
48 | All rights reserved.\n"; | |
49 | #endif /* not lint */ | |
50 | ||
51 | #ifndef lint | |
b8dff150 A |
52 | __unused static char rcsid[] = |
53 | "@(#) $Id: rarpd.c,v 1.3 2006/04/05 03:13:14 lindak Exp $"; | |
b7080c8e A |
54 | #endif |
55 | ||
56 | ||
57 | /* | |
58 | * rarpd - Reverse ARP Daemon | |
59 | * | |
60 | * Usage: rarpd -a [ -d -f ] | |
61 | * rarpd [ -d -f ] interface | |
62 | */ | |
63 | ||
64 | #include <stdio.h> | |
65 | #include <stdlib.h> | |
66 | #include <syslog.h> | |
67 | #include <string.h> | |
68 | #include <strings.h> | |
69 | #include <sys/types.h> | |
70 | #include <unistd.h> | |
71 | #include <sys/time.h> | |
72 | #include <net/bpf.h> | |
73 | #include <sys/socket.h> | |
74 | #include <sys/ioctl.h> | |
75 | #include <net/if.h> | |
76 | #include <net/if_dl.h> | |
77 | #include <net/if_types.h> | |
78 | #include <netinet/in.h> | |
79 | #include <netinet/if_ether.h> | |
80 | #include <sys/errno.h> | |
81 | #include <sys/file.h> | |
82 | #include <netdb.h> | |
83 | #include <arpa/inet.h> | |
84 | #include <dirent.h> | |
85 | ||
86 | #define FATAL 1 /* fatal error occurred */ | |
87 | #define NONFATAL 0 /* non fatal error occurred */ | |
88 | ||
89 | /* | |
90 | * The structure for each interface. | |
91 | */ | |
92 | struct if_info { | |
93 | int ii_fd; /* BPF file descriptor */ | |
94 | u_char ii_eaddr[6]; /* Ethernet address of this interface */ | |
9c859447 A |
95 | in_addr_t ii_ipaddr; /* IP address of this interface */ |
96 | in_addr_t ii_netmask; /* subnet or net mask */ | |
b7080c8e A |
97 | struct if_info *ii_next; |
98 | }; | |
99 | /* | |
100 | * The list of all interfaces that are being listened to. rarp_loop() | |
101 | * "selects" on the descriptors in this list. | |
102 | */ | |
103 | struct if_info *iflist; | |
104 | ||
105 | int rarp_open __P((char *)); | |
9c859447 | 106 | int rarp_bootable __P((in_addr_t)); |
b7080c8e A |
107 | void init_one __P((char *)); |
108 | void init_all __P((void)); | |
109 | void rarp_loop __P((void)); | |
110 | void lookup_eaddr __P((char *, u_char *)); | |
9c859447 | 111 | void lookup_ipaddr __P((char *, in_addr_t *, in_addr_t *)); |
b7080c8e A |
112 | void usage __P((void)); |
113 | void rarp_process __P((struct if_info *, u_char *)); | |
9c859447 A |
114 | void rarp_reply __P((struct if_info *, struct ether_header *, in_addr_t)); |
115 | void update_arptab __P((u_char *, in_addr_t)); | |
b7080c8e A |
116 | void err __P((int, const char *,...)); |
117 | void debug __P((const char *,...)); | |
9c859447 | 118 | in_addr_t ipaddrtonetmask __P((in_addr_t)); |
b7080c8e A |
119 | |
120 | int aflag = 0; /* listen on "all" interfaces */ | |
121 | int dflag = 0; /* print debugging messages */ | |
122 | int fflag = 0; /* don't fork */ | |
123 | ||
b8dff150 | 124 | int |
b7080c8e A |
125 | main(argc, argv) |
126 | int argc; | |
127 | char **argv; | |
128 | { | |
129 | int op, pid, devnull, f; | |
130 | char *ifname, *hostname, *name; | |
131 | ||
132 | extern char *optarg; | |
133 | extern int optind, opterr; | |
134 | ||
9c859447 | 135 | if ((name = strrchr(argv[0], '/')) != NULL) |
b7080c8e A |
136 | ++name; |
137 | else | |
138 | name = argv[0]; | |
139 | if (*name == '-') | |
140 | ++name; | |
141 | ||
142 | /* All error reporting is done through syslogs. */ | |
143 | openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON); | |
144 | ||
145 | opterr = 0; | |
146 | while ((op = getopt(argc, argv, "adf")) != EOF) { | |
147 | switch (op) { | |
148 | case 'a': | |
149 | ++aflag; | |
150 | break; | |
151 | ||
152 | case 'd': | |
153 | ++dflag; | |
154 | break; | |
155 | ||
156 | case 'f': | |
157 | ++fflag; | |
158 | break; | |
159 | ||
160 | default: | |
161 | usage(); | |
162 | /* NOTREACHED */ | |
163 | } | |
164 | } | |
165 | ifname = argv[optind++]; | |
166 | hostname = ifname ? argv[optind] : 0; | |
167 | if ((aflag && ifname) || (!aflag && ifname == 0)) | |
168 | usage(); | |
169 | ||
170 | if (aflag) | |
171 | init_all(); | |
172 | else | |
173 | init_one(ifname); | |
174 | ||
175 | if ((!fflag) && (!dflag)) { | |
176 | pid = fork(); | |
177 | if (pid > 0) | |
178 | /* Parent exits, leaving child in background. */ | |
179 | exit(0); | |
180 | else | |
181 | if (pid == -1) { | |
182 | err(FATAL, "cannot fork"); | |
183 | /* NOTREACHED */ | |
184 | } | |
185 | /* Fade into the background */ | |
186 | f = open("/dev/tty", O_RDWR); | |
187 | if (f >= 0) { | |
188 | if (ioctl(f, TIOCNOTTY, 0) < 0) { | |
189 | err(FATAL, "TIOCNOTTY: %s", strerror(errno)); | |
190 | /* NOTREACHED */ | |
191 | } | |
192 | (void) close(f); | |
193 | } | |
194 | (void) chdir("/"); | |
b8dff150 | 195 | (void) setpgid(0, getpid()); |
b7080c8e A |
196 | devnull = open("/dev/null", O_RDWR); |
197 | if (devnull >= 0) { | |
198 | (void) dup2(devnull, 0); | |
199 | (void) dup2(devnull, 1); | |
200 | (void) dup2(devnull, 2); | |
201 | if (devnull > 2) | |
202 | (void) close(devnull); | |
203 | } | |
204 | } | |
205 | rarp_loop(); | |
b8dff150 A |
206 | /* NOTREACHED */ |
207 | return 0; | |
b7080c8e A |
208 | } |
209 | /* | |
210 | * Add 'ifname' to the interface list. Lookup its IP address and network | |
211 | * mask and Ethernet address, and open a BPF file for it. | |
212 | */ | |
213 | void | |
214 | init_one(ifname) | |
215 | char *ifname; | |
216 | { | |
217 | struct if_info *p; | |
218 | ||
219 | p = (struct if_info *)malloc(sizeof(*p)); | |
220 | if (p == 0) { | |
221 | err(FATAL, "malloc: %s", strerror(errno)); | |
222 | /* NOTREACHED */ | |
223 | } | |
224 | p->ii_next = iflist; | |
225 | iflist = p; | |
226 | ||
227 | p->ii_fd = rarp_open(ifname); | |
228 | lookup_eaddr(ifname, p->ii_eaddr); | |
229 | lookup_ipaddr(ifname, &p->ii_ipaddr, &p->ii_netmask); | |
230 | } | |
231 | /* | |
232 | * Initialize all "candidate" interfaces that are in the system | |
233 | * configuration list. A "candidate" is up, not loopback and not | |
234 | * point to point. | |
235 | */ | |
236 | void | |
237 | init_all() | |
238 | { | |
239 | char inbuf[8192]; | |
240 | struct ifconf ifc; | |
241 | struct ifreq *ifr; | |
242 | int fd; | |
243 | int i, len; | |
244 | ||
245 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
246 | err(FATAL, "socket: %s", strerror(errno)); | |
247 | /* NOTREACHED */ | |
248 | } | |
249 | ||
250 | ifc.ifc_len = sizeof(inbuf); | |
251 | ifc.ifc_buf = inbuf; | |
252 | if (ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc) < 0 || | |
253 | ifc.ifc_len < sizeof(struct ifreq)) { | |
254 | err(FATAL, "init_all: SIOCGIFCONF: %s", strerror(errno)); | |
255 | /* NOTREACHED */ | |
256 | } | |
257 | ifr = ifc.ifc_req; | |
258 | for (i = 0; i < ifc.ifc_len; | |
259 | i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { | |
260 | len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; | |
261 | if (ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { | |
262 | err(FATAL, "init_all: SIOCGIFFLAGS: %s", | |
263 | strerror(errno)); | |
264 | /* NOTREACHED */ | |
265 | } | |
266 | if ((ifr->ifr_flags & | |
267 | (IFF_UP | IFF_LOOPBACK | IFF_POINTOPOINT)) != IFF_UP) | |
268 | continue; | |
269 | init_one(ifr->ifr_name); | |
270 | } | |
271 | (void) close(fd); | |
272 | } | |
273 | ||
274 | void | |
275 | usage() | |
276 | { | |
277 | (void) fprintf(stderr, "usage: rarpd -a [ -d -f ]\n"); | |
278 | (void) fprintf(stderr, " rarpd [ -d -f ] interface\n"); | |
279 | exit(1); | |
280 | } | |
281 | ||
282 | static int | |
283 | bpf_open() | |
284 | { | |
285 | int fd; | |
286 | int n = 0; | |
287 | char device[sizeof "/dev/bpf000"]; | |
288 | ||
289 | /* Go through all the minors and find one that isn't in use. */ | |
290 | do { | |
291 | (void) sprintf(device, "/dev/bpf%d", n++); | |
292 | fd = open(device, O_RDWR); | |
293 | } while (fd < 0 && errno == EBUSY); | |
294 | ||
295 | if (fd < 0) { | |
296 | err(FATAL, "%s: %s", device, strerror(errno)); | |
297 | /* NOTREACHED */ | |
298 | } | |
299 | return fd; | |
300 | } | |
301 | /* | |
302 | * Open a BPF file and attach it to the interface named 'device'. | |
303 | * Set immediate mode, and set a filter that accepts only RARP requests. | |
304 | */ | |
305 | int | |
306 | rarp_open(device) | |
307 | char *device; | |
308 | { | |
309 | int fd; | |
310 | struct ifreq ifr; | |
311 | u_int dlt; | |
312 | int immediate; | |
313 | ||
314 | static struct bpf_insn insns[] = { | |
315 | BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), | |
316 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_REVARP, 0, 3), | |
317 | BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20), | |
318 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REVREQUEST, 0, 1), | |
319 | BPF_STMT(BPF_RET | BPF_K, sizeof(struct ether_arp) + | |
320 | sizeof(struct ether_header)), | |
321 | BPF_STMT(BPF_RET | BPF_K, 0), | |
322 | }; | |
323 | static struct bpf_program filter = { | |
324 | sizeof insns / sizeof(insns[0]), | |
325 | insns | |
326 | }; | |
327 | ||
328 | fd = bpf_open(); | |
329 | ||
330 | /* Set immediate mode so packets are processed as they arrive. */ | |
331 | immediate = 1; | |
332 | if (ioctl(fd, BIOCIMMEDIATE, &immediate) < 0) { | |
333 | err(FATAL, "BIOCIMMEDIATE: %s", strerror(errno)); | |
334 | /* NOTREACHED */ | |
335 | } | |
336 | (void) strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name); | |
337 | if (ioctl(fd, BIOCSETIF, (caddr_t) & ifr) < 0) { | |
338 | err(FATAL, "BIOCSETIF: %s", strerror(errno)); | |
339 | /* NOTREACHED */ | |
340 | } | |
341 | /* Check that the data link layer is an Ethernet; this code won't work | |
342 | * with anything else. */ | |
343 | if (ioctl(fd, BIOCGDLT, (caddr_t) & dlt) < 0) { | |
344 | err(FATAL, "BIOCGDLT: %s", strerror(errno)); | |
345 | /* NOTREACHED */ | |
346 | } | |
347 | if (dlt != DLT_EN10MB) { | |
348 | err(FATAL, "%s is not an ethernet", device); | |
349 | /* NOTREACHED */ | |
350 | } | |
351 | /* Set filter program. */ | |
352 | if (ioctl(fd, BIOCSETF, (caddr_t) & filter) < 0) { | |
353 | err(FATAL, "BIOCSETF: %s", strerror(errno)); | |
354 | /* NOTREACHED */ | |
355 | } | |
356 | return fd; | |
357 | } | |
358 | /* | |
359 | * Perform various sanity checks on the RARP request packet. Return | |
360 | * false on failure and log the reason. | |
361 | */ | |
362 | static int | |
363 | rarp_check(p, len) | |
364 | u_char *p; | |
365 | int len; | |
366 | { | |
367 | struct ether_header *ep = (struct ether_header *) p; | |
368 | struct ether_arp *ap = (struct ether_arp *) (p + sizeof(*ep)); | |
369 | ||
370 | (void) debug("got a packet"); | |
371 | ||
372 | if (len < sizeof(*ep) + sizeof(*ap)) { | |
373 | err(NONFATAL, "truncated request"); | |
374 | return 0; | |
375 | } | |
376 | /* XXX This test might be better off broken out... */ | |
377 | if (ntohs (ep->ether_type) != ETHERTYPE_REVARP || | |
378 | ntohs (ap->arp_hrd) != ARPHRD_ETHER || | |
379 | ntohs (ap->arp_op) != ARPOP_REVREQUEST || | |
380 | ntohs (ap->arp_pro) != ETHERTYPE_IP || | |
381 | ap->arp_hln != 6 || ap->arp_pln != 4) { | |
382 | err(NONFATAL, "request fails sanity check"); | |
383 | return 0; | |
384 | } | |
385 | if (bcmp((char *) &ep->ether_shost, (char *) &ap->arp_sha, 6) != 0) { | |
386 | err(NONFATAL, "ether/arp sender address mismatch"); | |
387 | return 0; | |
388 | } | |
389 | if (bcmp((char *) &ap->arp_sha, (char *) &ap->arp_tha, 6) != 0) { | |
390 | err(NONFATAL, "ether/arp target address mismatch"); | |
391 | return 0; | |
392 | } | |
393 | return 1; | |
394 | } | |
395 | ||
396 | /* | |
397 | * Loop indefinitely listening for RARP requests on the | |
398 | * interfaces in 'iflist'. | |
399 | */ | |
400 | void | |
401 | rarp_loop() | |
402 | { | |
403 | u_char *buf, *bp, *ep; | |
404 | int cc, fd; | |
405 | fd_set fds, listeners; | |
406 | int bufsize, maxfd = 0; | |
407 | struct if_info *ii; | |
408 | ||
409 | if (iflist == 0) { | |
410 | err(FATAL, "no interfaces"); | |
411 | /* NOTREACHED */ | |
412 | } | |
413 | if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t) & bufsize) < 0) { | |
414 | err(FATAL, "BIOCGBLEN: %s", strerror(errno)); | |
415 | /* NOTREACHED */ | |
416 | } | |
417 | buf = (u_char *) malloc((unsigned) bufsize); | |
418 | if (buf == 0) { | |
419 | err(FATAL, "malloc: %s", strerror(errno)); | |
420 | /* NOTREACHED */ | |
421 | } | |
422 | /* | |
423 | * Find the highest numbered file descriptor for select(). | |
424 | * Initialize the set of descriptors to listen to. | |
425 | */ | |
426 | FD_ZERO(&fds); | |
427 | for (ii = iflist; ii; ii = ii->ii_next) { | |
428 | FD_SET(ii->ii_fd, &fds); | |
429 | if (ii->ii_fd > maxfd) | |
430 | maxfd = ii->ii_fd; | |
431 | } | |
432 | while (1) { | |
433 | listeners = fds; | |
434 | if (select(maxfd + 1, &listeners, (struct fd_set *) 0, | |
435 | (struct fd_set *) 0, (struct timeval *) 0) < 0) { | |
436 | err(FATAL, "select: %s", strerror(errno)); | |
437 | /* NOTREACHED */ | |
438 | } | |
439 | for (ii = iflist; ii; ii = ii->ii_next) { | |
440 | fd = ii->ii_fd; | |
441 | if (!FD_ISSET(fd, &listeners)) | |
442 | continue; | |
443 | again: | |
444 | cc = read(fd, (char *) buf, bufsize); | |
445 | /* Don't choke when we get ptraced */ | |
446 | if (cc < 0 && errno == EINTR) | |
447 | goto again; | |
448 | /* Due to a SunOS bug, after 2^31 bytes, the file | |
449 | * offset overflows and read fails with EINVAL. The | |
450 | * lseek() to 0 will fix things. */ | |
451 | if (cc < 0) { | |
452 | if (errno == EINVAL && | |
453 | (lseek(fd, 0, SEEK_CUR) + bufsize) < 0) { | |
454 | (void) lseek(fd, 0, 0); | |
455 | goto again; | |
456 | } | |
457 | err(FATAL, "read: %s", strerror(errno)); | |
458 | /* NOTREACHED */ | |
459 | } | |
460 | /* Loop through the packet(s) */ | |
461 | #define bhp ((struct bpf_hdr *)bp) | |
462 | bp = buf; | |
463 | ep = bp + cc; | |
464 | while (bp < ep) { | |
465 | register int caplen, hdrlen; | |
466 | ||
467 | caplen = bhp->bh_caplen; | |
468 | hdrlen = bhp->bh_hdrlen; | |
469 | if (rarp_check(bp + hdrlen, caplen)) | |
470 | rarp_process(ii, bp + hdrlen); | |
471 | bp += BPF_WORDALIGN(hdrlen + caplen); | |
472 | } | |
473 | } | |
474 | } | |
475 | } | |
476 | #ifndef TFTP_DIR | |
477 | #define TFTP_DIR "/tftpboot" | |
478 | #endif | |
479 | ||
480 | /* | |
481 | * True if this server can boot the host whose IP address is 'addr'. | |
482 | * This check is made by looking in the tftp directory for the | |
483 | * configuration file. | |
484 | */ | |
485 | int | |
486 | rarp_bootable(addr) | |
9c859447 | 487 | in_addr_t addr; |
b7080c8e A |
488 | { |
489 | register struct dirent *dent; | |
490 | register DIR *d; | |
491 | char ipname[9]; | |
492 | static DIR *dd = 0; | |
493 | ||
9c859447 | 494 | (void) sprintf(ipname, "%08X", addr); |
b7080c8e | 495 | /* If directory is already open, rewind it. Otherwise, open it. */ |
9c859447 | 496 | if ((d = dd) != NULL) |
b7080c8e A |
497 | rewinddir(d); |
498 | else { | |
499 | if (chdir(TFTP_DIR) == -1) { | |
500 | err(FATAL, "chdir: %s", strerror(errno)); | |
501 | /* NOTREACHED */ | |
502 | } | |
503 | d = opendir("."); | |
504 | if (d == 0) { | |
505 | err(FATAL, "opendir: %s", strerror(errno)); | |
506 | /* NOTREACHED */ | |
507 | } | |
508 | dd = d; | |
509 | } | |
9c859447 | 510 | while ((dent = readdir(d)) != NULL) |
b7080c8e A |
511 | if (strncmp(dent->d_name, ipname, 8) == 0) |
512 | return 1; | |
513 | return 0; | |
514 | } | |
515 | /* | |
516 | * Given a list of IP addresses, 'alist', return the first address that | |
517 | * is on network 'net'; 'netmask' is a mask indicating the network portion | |
518 | * of the address. | |
519 | */ | |
9c859447 | 520 | in_addr_t |
b7080c8e | 521 | choose_ipaddr(alist, net, netmask) |
9c859447 A |
522 | in_addr_t **alist; |
523 | in_addr_t net; | |
524 | in_addr_t netmask; | |
b7080c8e A |
525 | { |
526 | for (; *alist; ++alist) { | |
527 | if ((**alist & netmask) == net) | |
528 | return **alist; | |
529 | } | |
530 | return 0; | |
531 | } | |
532 | /* | |
533 | * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has | |
534 | * already been checked for validity. The reply is overlaid on the request. | |
535 | */ | |
536 | void | |
537 | rarp_process(ii, pkt) | |
538 | struct if_info *ii; | |
539 | u_char *pkt; | |
540 | { | |
541 | struct ether_header *ep; | |
542 | struct hostent *hp; | |
9c859447 | 543 | in_addr_t target_ipaddr; |
b7080c8e A |
544 | char ename[256]; |
545 | struct in_addr in; | |
546 | ||
547 | ep = (struct ether_header *) pkt; | |
548 | ||
b8dff150 | 549 | if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0 || |
b7080c8e A |
550 | (hp = gethostbyname(ename)) == 0) |
551 | return; | |
552 | ||
553 | /* Choose correct address from list. */ | |
554 | if (hp->h_addrtype != AF_INET) { | |
555 | err(FATAL, "cannot handle non IP addresses"); | |
556 | /* NOTREACHED */ | |
557 | } | |
9c859447 | 558 | target_ipaddr = choose_ipaddr((in_addr_t **) hp->h_addr_list, |
b7080c8e A |
559 | ii->ii_ipaddr & ii->ii_netmask, ii->ii_netmask); |
560 | ||
561 | if (target_ipaddr == 0) { | |
562 | in.s_addr = ii->ii_ipaddr & ii->ii_netmask; | |
563 | err(NONFATAL, "cannot find %s on net %s\n", | |
564 | ename, inet_ntoa(in)); | |
565 | return; | |
566 | } | |
567 | if (rarp_bootable(htonl(target_ipaddr))) | |
568 | rarp_reply(ii, ep, target_ipaddr); | |
569 | } | |
570 | /* | |
571 | * Lookup the ethernet address of the interface attached to the BPF | |
572 | * file descriptor 'fd'; return it in 'eaddr'. | |
573 | */ | |
574 | void | |
575 | lookup_eaddr(ifname, eaddr) | |
576 | char *ifname; | |
577 | u_char *eaddr; | |
578 | { | |
579 | char inbuf[8192]; | |
580 | struct ifconf ifc; | |
581 | struct ifreq *ifr; | |
582 | struct sockaddr_dl *sdl; | |
583 | int fd; | |
584 | int i, len; | |
585 | ||
586 | /* We cannot use SIOCGIFADDR on the BPF descriptor. | |
587 | We must instead get all the interfaces with SIOCGIFCONF | |
588 | and find the right one. */ | |
589 | ||
590 | /* Use datagram socket to get Ethernet address. */ | |
591 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
592 | err(FATAL, "socket: %s", strerror(errno)); | |
593 | /* NOTREACHED */ | |
594 | } | |
595 | ||
596 | ifc.ifc_len = sizeof(inbuf); | |
597 | ifc.ifc_buf = inbuf; | |
598 | if (ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc) < 0 || | |
599 | ifc.ifc_len < sizeof(struct ifreq)) { | |
600 | err(FATAL, "lookup_eaddr: SIOGIFCONF: %s", strerror(errno)); | |
601 | /* NOTREACHED */ | |
602 | } | |
603 | ifr = ifc.ifc_req; | |
604 | for (i = 0; i < ifc.ifc_len; | |
605 | i += len, ifr = (struct ifreq *)((caddr_t)ifr + len)) { | |
606 | len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; | |
607 | sdl = (struct sockaddr_dl *)&ifr->ifr_addr; | |
608 | if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER || | |
609 | sdl->sdl_alen != 6) | |
610 | continue; | |
611 | if (!strncmp(ifr->ifr_name, ifname, sizeof(ifr->ifr_name))) { | |
612 | bcopy((caddr_t)LLADDR(sdl), (caddr_t)eaddr, 6); | |
613 | if (dflag) | |
614 | fprintf(stderr, "%s: %x:%x:%x:%x:%x:%x\n", | |
615 | ifr->ifr_name, eaddr[0], eaddr[1], | |
616 | eaddr[2], eaddr[3], eaddr[4], eaddr[5]); | |
617 | return; | |
618 | } | |
619 | } | |
620 | ||
621 | err(FATAL, "lookup_eaddr: Never saw interface `%s'!", ifname); | |
622 | } | |
623 | /* | |
624 | * Lookup the IP address and network mask of the interface named 'ifname'. | |
625 | */ | |
626 | void | |
627 | lookup_ipaddr(ifname, addrp, netmaskp) | |
628 | char *ifname; | |
9c859447 A |
629 | in_addr_t *addrp; |
630 | in_addr_t *netmaskp; | |
b7080c8e A |
631 | { |
632 | int fd; | |
633 | struct ifreq ifr; | |
634 | ||
635 | /* Use datagram socket to get IP address. */ | |
636 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
637 | err(FATAL, "socket: %s", strerror(errno)); | |
638 | /* NOTREACHED */ | |
639 | } | |
640 | (void) strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); | |
641 | if (ioctl(fd, SIOCGIFADDR, (char *) &ifr) < 0) { | |
642 | err(FATAL, "SIOCGIFADDR: %s", strerror(errno)); | |
643 | /* NOTREACHED */ | |
644 | } | |
645 | *addrp = ((struct sockaddr_in *) & ifr.ifr_addr)->sin_addr.s_addr; | |
646 | if (ioctl(fd, SIOCGIFNETMASK, (char *) &ifr) < 0) { | |
647 | perror("SIOCGIFNETMASK"); | |
648 | exit(1); | |
649 | } | |
650 | *netmaskp = ((struct sockaddr_in *) & ifr.ifr_addr)->sin_addr.s_addr; | |
651 | /* If SIOCGIFNETMASK didn't work, figure out a mask from the IP | |
652 | * address class. */ | |
653 | if (*netmaskp == 0) | |
654 | *netmaskp = ipaddrtonetmask(*addrp); | |
655 | ||
656 | (void) close(fd); | |
657 | } | |
658 | /* | |
659 | * Poke the kernel arp tables with the ethernet/ip address combinataion | |
660 | * given. When processing a reply, we must do this so that the booting | |
661 | * host (i.e. the guy running rarpd), won't try to ARP for the hardware | |
662 | * address of the guy being booted (he cannot answer the ARP). | |
663 | */ | |
664 | void | |
665 | update_arptab(ep, ipaddr) | |
666 | u_char *ep; | |
9c859447 | 667 | in_addr_t ipaddr; |
b7080c8e | 668 | { |
b8dff150 | 669 | //int s; |
b7080c8e A |
670 | struct arpreq request; |
671 | struct sockaddr_in *sin; | |
672 | ||
673 | request.arp_flags = 0; | |
674 | sin = (struct sockaddr_in *) & request.arp_pa; | |
675 | sin->sin_family = AF_INET; | |
676 | sin->sin_addr.s_addr = ipaddr; | |
677 | request.arp_ha.sa_family = AF_UNSPEC; | |
678 | /* This is needed #if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN, | |
679 | because AF_UNSPEC is zero and the kernel assumes that a zero | |
680 | sa_family means that the real sa_family value is in sa_len. */ | |
681 | request.arp_ha.sa_len = 16; /* XXX */ | |
682 | bcopy((char *) ep, (char *) request.arp_ha.sa_data, 6); | |
683 | ||
684 | #if 0 | |
685 | s = socket(AF_INET, SOCK_DGRAM, 0); | |
686 | if (ioctl(s, SIOCSARP, (caddr_t) & request) < 0) { | |
687 | err(NONFATAL, "SIOCSARP: %s", strerror(errno)); | |
688 | } | |
689 | (void) close(s); | |
690 | #endif | |
691 | } | |
692 | /* | |
693 | * Build a reverse ARP packet and sent it out on the interface. | |
694 | * 'ep' points to a valid ARPOP_REVREQUEST. The ARPOP_REVREPLY is built | |
695 | * on top of the request, then written to the network. | |
696 | * | |
697 | * RFC 903 defines the ether_arp fields as follows. The following comments | |
698 | * are taken (more or less) straight from this document. | |
699 | * | |
700 | * ARPOP_REVREQUEST | |
701 | * | |
702 | * arp_sha is the hardware address of the sender of the packet. | |
703 | * arp_spa is undefined. | |
704 | * arp_tha is the 'target' hardware address. | |
705 | * In the case where the sender wishes to determine his own | |
706 | * protocol address, this, like arp_sha, will be the hardware | |
707 | * address of the sender. | |
708 | * arp_tpa is undefined. | |
709 | * | |
710 | * ARPOP_REVREPLY | |
711 | * | |
712 | * arp_sha is the hardware address of the responder (the sender of the | |
713 | * reply packet). | |
714 | * arp_spa is the protocol address of the responder (see the note below). | |
715 | * arp_tha is the hardware address of the target, and should be the same as | |
716 | * that which was given in the request. | |
717 | * arp_tpa is the protocol address of the target, that is, the desired address. | |
718 | * | |
719 | * Note that the requirement that arp_spa be filled in with the responder's | |
720 | * protocol is purely for convenience. For instance, if a system were to use | |
721 | * both ARP and RARP, then the inclusion of the valid protocol-hardware | |
722 | * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent | |
723 | * ARP request. | |
724 | */ | |
725 | void | |
726 | rarp_reply(ii, ep, ipaddr) | |
727 | struct if_info *ii; | |
728 | struct ether_header *ep; | |
9c859447 | 729 | in_addr_t ipaddr; |
b7080c8e A |
730 | { |
731 | int n; | |
732 | struct ether_arp *ap = (struct ether_arp *) (ep + 1); | |
733 | int len; | |
734 | ||
735 | update_arptab((u_char *) & ap->arp_sha, ipaddr); | |
736 | ||
737 | /* Build the rarp reply by modifying the rarp request in place. */ | |
738 | ep->ether_type = htons(ETHERTYPE_REVARP); | |
739 | ap->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); | |
740 | ap->ea_hdr.ar_pro = htons(ETHERTYPE_IP); | |
741 | ap->arp_op = htons(ARPOP_REVREPLY); | |
742 | ||
743 | bcopy((char *) &ap->arp_sha, (char *) &ep->ether_dhost, 6); | |
744 | bcopy((char *) ii->ii_eaddr, (char *) &ep->ether_shost, 6); | |
745 | bcopy((char *) ii->ii_eaddr, (char *) &ap->arp_sha, 6); | |
746 | ||
747 | bcopy((char *) &ipaddr, (char *) ap->arp_tpa, 4); | |
748 | /* Target hardware is unchanged. */ | |
749 | bcopy((char *) &ii->ii_ipaddr, (char *) ap->arp_spa, 4); | |
750 | ||
751 | len = sizeof(*ep) + sizeof(*ap); | |
752 | n = write(ii->ii_fd, (char *) ep, len); | |
753 | if (n != len) { | |
754 | err(NONFATAL, "write: only %d of %d bytes written", n, len); | |
755 | } | |
756 | } | |
757 | /* | |
758 | * Get the netmask of an IP address. This routine is used if | |
759 | * SIOCGIFNETMASK doesn't work. | |
760 | */ | |
9c859447 | 761 | in_addr_t |
b7080c8e | 762 | ipaddrtonetmask(addr) |
9c859447 | 763 | in_addr_t addr; |
b7080c8e A |
764 | { |
765 | if (IN_CLASSA(addr)) | |
766 | return IN_CLASSA_NET; | |
767 | if (IN_CLASSB(addr)) | |
768 | return IN_CLASSB_NET; | |
769 | if (IN_CLASSC(addr)) | |
770 | return IN_CLASSC_NET; | |
771 | err(FATAL, "unknown IP address class: %08X", addr); | |
772 | /* NOTREACHED */ | |
b8dff150 | 773 | return 0; |
b7080c8e A |
774 | } |
775 | ||
776 | #if __STDC__ | |
777 | #include <stdarg.h> | |
778 | #else | |
779 | #include <varargs.h> | |
780 | #endif | |
781 | ||
782 | void | |
783 | #if __STDC__ | |
784 | err(int fatal, const char *fmt,...) | |
785 | #else | |
786 | err(fmt, va_alist) | |
787 | int fatal; | |
788 | char *fmt; | |
789 | va_dcl | |
790 | #endif | |
791 | { | |
792 | va_list ap; | |
793 | #if __STDC__ | |
794 | va_start(ap, fmt); | |
795 | #else | |
796 | va_start(ap); | |
797 | #endif | |
798 | if (dflag) { | |
799 | if (fatal) | |
800 | (void) fprintf(stderr, "rarpd: error: "); | |
801 | else | |
802 | (void) fprintf(stderr, "rarpd: warning: "); | |
803 | (void) vfprintf(stderr, fmt, ap); | |
804 | (void) fprintf(stderr, "\n"); | |
805 | } | |
806 | vsyslog(LOG_ERR, fmt, ap); | |
807 | va_end(ap); | |
808 | if (fatal) | |
809 | exit(1); | |
810 | /* NOTREACHED */ | |
811 | } | |
812 | ||
813 | void | |
814 | #if __STDC__ | |
815 | debug(const char *fmt,...) | |
816 | #else | |
817 | debug(fmt, va_alist) | |
818 | char *fmt; | |
819 | va_dcl | |
820 | #endif | |
821 | { | |
822 | va_list ap; | |
823 | ||
824 | if (dflag) { | |
825 | #if __STDC__ | |
826 | va_start(ap, fmt); | |
827 | #else | |
828 | va_start(ap); | |
829 | #endif | |
830 | (void) fprintf(stderr, "rarpd: "); | |
831 | (void) vfprintf(stderr, fmt, ap); | |
832 | va_end(ap); | |
833 | (void) fprintf(stderr, "\n"); | |
834 | } | |
835 | } |