| 1 | /* |
| 2 | * Copyright (c) 1988, 1992 The University of Utah and the Center |
| 3 | * for Software Science (CSS). |
| 4 | * Copyright (c) 1992, 1993 |
| 5 | * The Regents of the University of California. All rights reserved. |
| 6 | * |
| 7 | * This code is derived from software contributed to Berkeley by |
| 8 | * the Center for Software Science of the University of Utah Computer |
| 9 | * Science Department. CSS requests users of this software to return |
| 10 | * to css-dist@cs.utah.edu any improvements that they make and grant |
| 11 | * CSS redistribution rights. |
| 12 | * |
| 13 | * Redistribution and use in source and binary forms, with or without |
| 14 | * modification, are permitted provided that the following conditions |
| 15 | * are met: |
| 16 | * 1. Redistributions of source code must retain the above copyright |
| 17 | * notice, this list of conditions and the following disclaimer. |
| 18 | * 2. Redistributions in binary form must reproduce the above copyright |
| 19 | * notice, this list of conditions and the following disclaimer in the |
| 20 | * documentation and/or other materials provided with the distribution. |
| 21 | * 3. All advertising materials mentioning features or use of this software |
| 22 | * must display the following acknowledgement: |
| 23 | * This product includes software developed by the University of |
| 24 | * California, Berkeley and its contributors. |
| 25 | * 4. Neither the name of the University nor the names of its contributors |
| 26 | * may be used to endorse or promote products derived from this software |
| 27 | * without specific prior written permission. |
| 28 | * |
| 29 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 30 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 31 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 32 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 33 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 34 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 35 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 36 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 37 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 38 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 39 | * SUCH DAMAGE. |
| 40 | * |
| 41 | * from: @(#)bpf.c 8.1 (Berkeley) 6/4/93 |
| 42 | * |
| 43 | * From: Utah Hdr: bpf.c 3.1 92/07/06 |
| 44 | * Author: Jeff Forys, University of Utah CSS |
| 45 | * |
| 46 | * @(#)bpf.c 8.1 (Berkeley) 6/4/93 |
| 47 | * $FreeBSD: src/libexec/rbootd/bpf.c,v 1.10 1999/08/28 00:09:44 peter Exp $ |
| 48 | */ |
| 49 | |
| 50 | #include <sys/param.h> |
| 51 | #include <sys/ioctl.h> |
| 52 | #include <sys/socket.h> |
| 53 | #include <sys/time.h> |
| 54 | |
| 55 | #include <net/if.h> |
| 56 | #include <net/bpf.h> |
| 57 | |
| 58 | #include <ctype.h> |
| 59 | #include <errno.h> |
| 60 | #include <fcntl.h> |
| 61 | #include <stdio.h> |
| 62 | #include <stdlib.h> |
| 63 | #include <string.h> |
| 64 | #include <syslog.h> |
| 65 | #include <unistd.h> |
| 66 | #include "defs.h" |
| 67 | #include "pathnames.h" |
| 68 | |
| 69 | static int BpfFd = -1; |
| 70 | static unsigned BpfLen = 0; |
| 71 | static u_int8_t *BpfPkt = NULL; |
| 72 | |
| 73 | /* |
| 74 | ** BpfOpen -- Open and initialize a BPF device. |
| 75 | ** |
| 76 | ** Parameters: |
| 77 | ** None. |
| 78 | ** |
| 79 | ** Returns: |
| 80 | ** File descriptor of opened BPF device (for select() etc). |
| 81 | ** |
| 82 | ** Side Effects: |
| 83 | ** If an error is encountered, the program terminates here. |
| 84 | */ |
| 85 | int |
| 86 | BpfOpen(void) |
| 87 | { |
| 88 | struct ifreq ifr; |
| 89 | char bpfdev[32]; |
| 90 | int n = 0; |
| 91 | |
| 92 | /* |
| 93 | * Open the first available BPF device. |
| 94 | */ |
| 95 | do { |
| 96 | (void) sprintf(bpfdev, _PATH_BPF, n++); |
| 97 | BpfFd = open(bpfdev, O_RDWR); |
| 98 | } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM)); |
| 99 | |
| 100 | if (BpfFd < 0) { |
| 101 | syslog(LOG_ERR, "bpf: no available devices: %m"); |
| 102 | Exit(0); |
| 103 | } |
| 104 | |
| 105 | /* |
| 106 | * Set interface name for bpf device, get data link layer |
| 107 | * type and make sure it's type Ethernet. |
| 108 | */ |
| 109 | (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); |
| 110 | if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { |
| 111 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); |
| 112 | Exit(0); |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | * Make sure we are dealing with an Ethernet device. |
| 117 | */ |
| 118 | if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { |
| 119 | syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); |
| 120 | Exit(0); |
| 121 | } |
| 122 | if (n != DLT_EN10MB) { |
| 123 | syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", |
| 124 | IntfName, n); |
| 125 | Exit(0); |
| 126 | } |
| 127 | |
| 128 | /* |
| 129 | * On read(), return packets immediately (do not buffer them). |
| 130 | */ |
| 131 | n = 1; |
| 132 | if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { |
| 133 | syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); |
| 134 | Exit(0); |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | * Try to enable the chip/driver's multicast address filter to |
| 139 | * grab our RMP address. If this fails, try promiscuous mode. |
| 140 | * If this fails, there's no way we are going to get any RMP |
| 141 | * packets so just exit here. |
| 142 | */ |
| 143 | #ifdef MSG_EOR |
| 144 | ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; |
| 145 | #endif |
| 146 | ifr.ifr_addr.sa_family = AF_UNSPEC; |
| 147 | memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); |
| 148 | if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { |
| 149 | syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); |
| 150 | Exit(0); |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | * Ask BPF how much buffer space it requires and allocate one. |
| 155 | */ |
| 156 | if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { |
| 157 | syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); |
| 158 | Exit(0); |
| 159 | } |
| 160 | if (BpfPkt == NULL) |
| 161 | BpfPkt = (u_int8_t *)malloc(BpfLen); |
| 162 | |
| 163 | if (BpfPkt == NULL) { |
| 164 | syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", |
| 165 | BpfLen); |
| 166 | Exit(0); |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | * Write a little program to snarf RMP Boot packets and stuff |
| 171 | * it down BPF's throat (i.e. set up the packet filter). |
| 172 | */ |
| 173 | { |
| 174 | #define RMP ((struct rmp_packet *)0) |
| 175 | static struct bpf_insn bpf_insn[] = { |
| 176 | { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, |
| 177 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, |
| 178 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, |
| 179 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, |
| 180 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, |
| 181 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, |
| 182 | { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, |
| 183 | { BPF_RET|BPF_K, 0, 0, 0x0 } |
| 184 | }; |
| 185 | #undef RMP |
| 186 | static struct bpf_program bpf_pgm = { |
| 187 | sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn |
| 188 | }; |
| 189 | |
| 190 | if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { |
| 191 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); |
| 192 | Exit(0); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | return(BpfFd); |
| 197 | } |
| 198 | |
| 199 | /* |
| 200 | ** BPF GetIntfName -- Return the name of a network interface attached to |
| 201 | ** the system, or 0 if none can be found. The interface |
| 202 | ** must be configured up; the lowest unit number is |
| 203 | ** preferred; loopback is ignored. |
| 204 | ** |
| 205 | ** Parameters: |
| 206 | ** errmsg - if no network interface found, *errmsg explains why. |
| 207 | ** |
| 208 | ** Returns: |
| 209 | ** A (static) pointer to interface name, or NULL on error. |
| 210 | ** |
| 211 | ** Side Effects: |
| 212 | ** None. |
| 213 | */ |
| 214 | char * |
| 215 | BpfGetIntfName(char **errmsg) |
| 216 | { |
| 217 | struct ifreq ibuf[8], *ifrp, *ifend, *mp; |
| 218 | struct ifconf ifc; |
| 219 | int fd; |
| 220 | int minunit, n; |
| 221 | char *cp; |
| 222 | static char device[sizeof(ifrp->ifr_name)]; |
| 223 | static char errbuf[128] = "No Error!"; |
| 224 | |
| 225 | if (errmsg != NULL) |
| 226 | *errmsg = errbuf; |
| 227 | |
| 228 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
| 229 | (void) strcpy(errbuf, "bpf: socket: %m"); |
| 230 | return(NULL); |
| 231 | } |
| 232 | ifc.ifc_len = sizeof ibuf; |
| 233 | ifc.ifc_buf = (caddr_t)ibuf; |
| 234 | |
| 235 | #ifdef OSIOCGIFCONF |
| 236 | if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 || |
| 237 | ifc.ifc_len < sizeof(struct ifreq)) { |
| 238 | (void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m"); |
| 239 | return(NULL); |
| 240 | } |
| 241 | #else |
| 242 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || |
| 243 | ifc.ifc_len < sizeof(struct ifreq)) { |
| 244 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); |
| 245 | return(NULL); |
| 246 | } |
| 247 | #endif |
| 248 | ifrp = ibuf; |
| 249 | ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); |
| 250 | |
| 251 | mp = NULL; |
| 252 | minunit = 666; |
| 253 | for (; ifrp < ifend; ++ifrp) { |
| 254 | if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { |
| 255 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); |
| 256 | return(NULL); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | * If interface is down or this is the loopback interface, |
| 261 | * ignore it. |
| 262 | */ |
| 263 | if ((ifrp->ifr_flags & IFF_UP) == 0 || |
| 264 | #ifdef IFF_LOOPBACK |
| 265 | (ifrp->ifr_flags & IFF_LOOPBACK)) |
| 266 | #else |
| 267 | (strcmp(ifrp->ifr_name, "lo0") == 0)) |
| 268 | #endif |
| 269 | continue; |
| 270 | |
| 271 | for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) |
| 272 | ; |
| 273 | n = atoi(cp); |
| 274 | if (n < minunit) { |
| 275 | minunit = n; |
| 276 | mp = ifrp; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | (void) close(fd); |
| 281 | if (mp == NULL) { |
| 282 | (void) strcpy(errbuf, "bpf: no interfaces found"); |
| 283 | return(NULL); |
| 284 | } |
| 285 | |
| 286 | (void) strcpy(device, mp->ifr_name); |
| 287 | return(device); |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | ** BpfRead -- Read packets from a BPF device and fill in `rconn'. |
| 292 | ** |
| 293 | ** Parameters: |
| 294 | ** rconn - filled in with next packet. |
| 295 | ** doread - is True if we can issue a read() syscall. |
| 296 | ** |
| 297 | ** Returns: |
| 298 | ** True if `rconn' contains a new packet, False otherwise. |
| 299 | ** |
| 300 | ** Side Effects: |
| 301 | ** None. |
| 302 | */ |
| 303 | int |
| 304 | BpfRead(RMPCONN *rconn, int doread) |
| 305 | { |
| 306 | int datlen, caplen, hdrlen; |
| 307 | static u_int8_t *bp = NULL, *ep = NULL; |
| 308 | int cc; |
| 309 | |
| 310 | /* |
| 311 | * The read() may block, or it may return one or more packets. |
| 312 | * We let the caller decide whether or not we can issue a read(). |
| 313 | */ |
| 314 | if (doread) { |
| 315 | if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { |
| 316 | syslog(LOG_ERR, "bpf: read: %m"); |
| 317 | return(0); |
| 318 | } else { |
| 319 | bp = BpfPkt; |
| 320 | ep = BpfPkt + cc; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | #define bhp ((struct bpf_hdr *)bp) |
| 325 | /* |
| 326 | * If there is a new packet in the buffer, stuff it into `rconn' |
| 327 | * and return a success indication. |
| 328 | */ |
| 329 | if (bp < ep) { |
| 330 | datlen = bhp->bh_datalen; |
| 331 | caplen = bhp->bh_caplen; |
| 332 | hdrlen = bhp->bh_hdrlen; |
| 333 | |
| 334 | if (caplen != datlen) |
| 335 | syslog(LOG_ERR, |
| 336 | "bpf: short packet dropped (%d of %d bytes)", |
| 337 | caplen, datlen); |
| 338 | else if (caplen > sizeof(struct rmp_packet)) |
| 339 | syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", |
| 340 | caplen); |
| 341 | else { |
| 342 | rconn->rmplen = caplen; |
| 343 | memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp, |
| 344 | sizeof(struct timeval)); |
| 345 | memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen); |
| 346 | } |
| 347 | bp += BPF_WORDALIGN(caplen + hdrlen); |
| 348 | return(1); |
| 349 | } |
| 350 | #undef bhp |
| 351 | |
| 352 | return(0); |
| 353 | } |
| 354 | |
| 355 | /* |
| 356 | ** BpfWrite -- Write packet to BPF device. |
| 357 | ** |
| 358 | ** Parameters: |
| 359 | ** rconn - packet to send. |
| 360 | ** |
| 361 | ** Returns: |
| 362 | ** True if write succeeded, False otherwise. |
| 363 | ** |
| 364 | ** Side Effects: |
| 365 | ** None. |
| 366 | */ |
| 367 | int |
| 368 | BpfWrite(RMPCONN *rconn) |
| 369 | { |
| 370 | if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { |
| 371 | syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); |
| 372 | return(0); |
| 373 | } |
| 374 | |
| 375 | return(1); |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** BpfClose -- Close a BPF device. |
| 380 | ** |
| 381 | ** Parameters: |
| 382 | ** None. |
| 383 | ** |
| 384 | ** Returns: |
| 385 | ** Nothing. |
| 386 | ** |
| 387 | ** Side Effects: |
| 388 | ** None. |
| 389 | */ |
| 390 | void |
| 391 | BpfClose(void) |
| 392 | { |
| 393 | struct ifreq ifr; |
| 394 | |
| 395 | if (BpfPkt != NULL) { |
| 396 | free((char *)BpfPkt); |
| 397 | BpfPkt = NULL; |
| 398 | } |
| 399 | |
| 400 | if (BpfFd == -1) |
| 401 | return; |
| 402 | |
| 403 | #ifdef MSG_EOR |
| 404 | ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; |
| 405 | #endif |
| 406 | ifr.ifr_addr.sa_family = AF_UNSPEC; |
| 407 | memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); |
| 408 | if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0) |
| 409 | (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0); |
| 410 | |
| 411 | (void) close(BpfFd); |
| 412 | BpfFd = -1; |
| 413 | } |