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