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