1 1.8 christos /* $NetBSD: ndbootd-bpf.c,v 1.8 2004/12/01 23:18:20 christos Exp $ */ 2 1.2 fredette 3 1.1 fredette /* ndbootd-bpf.c - the Sun Network Disk (nd) daemon BPF component: */ 4 1.1 fredette 5 1.1 fredette /* 6 1.1 fredette * Copyright (c) 2001 Matthew Fredette. All rights reserved. 7 1.1 fredette * 8 1.1 fredette * Redistribution and use in source and binary forms, with or without 9 1.1 fredette * modification, are permitted provided that the following conditions 10 1.1 fredette * are met: 11 1.1 fredette * 1. Redistributions of source code must retain the above copyright 12 1.1 fredette * notice, this list of conditions and the following disclaimer. 13 1.1 fredette * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 fredette * notice, this list of conditions and the following disclaimer in the 15 1.1 fredette * documentation and/or other materials provided with the distribution. 16 1.1 fredette * 3. All advertising materials mentioning features or use of this software 17 1.1 fredette * must display the following acknowledgement: 18 1.1 fredette * This product includes software developed by Matthew Fredette. 19 1.1 fredette * 4. The name of Matthew Fredette may not be used to endorse or promote 20 1.1 fredette * products derived from this software without specific prior written 21 1.1 fredette * permission. 22 1.1 fredette * 23 1.1 fredette * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 24 1.1 fredette * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 25 1.1 fredette * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26 1.1 fredette */ 27 1.1 fredette 28 1.3 fredette /* <<Header: /data/home/fredette/project/THE-WEIGHT-CVS/ndbootd/config/ndbootd-bpf.c,v 1.4 2001/05/23 02:35:49 fredette Exp >> */ 29 1.1 fredette 30 1.1 fredette /* 31 1.1 fredette * <<Log: ndbootd-bpf.c,v >> 32 1.3 fredette * Revision 1.4 2001/05/23 02:35:49 fredette 33 1.3 fredette * Changed many debugging printfs to compile quietly on the 34 1.3 fredette * alpha. Patch from Andrew Brown <atatat (at) atatdot.net>. 35 1.3 fredette * 36 1.1 fredette * Revision 1.3 2001/05/22 13:13:24 fredette 37 1.1 fredette * Ran indent(1) with NetBSD's KNF-approximating profile. 38 1.1 fredette * 39 1.1 fredette * Revision 1.2 2001/05/09 20:50:46 fredette 40 1.1 fredette * Removed an unnecessary comment. 41 1.1 fredette * 42 1.1 fredette * Revision 1.1 2001/01/29 15:12:13 fredette 43 1.1 fredette * Added. 44 1.1 fredette * 45 1.1 fredette */ 46 1.1 fredette 47 1.4 thorpej #include <sys/cdefs.h> 48 1.4 thorpej #if o 49 1.3 fredette static const char _ndbootd_bpf_c_rcsid[] = "<<Id: ndbootd-bpf.c,v 1.4 2001/05/23 02:35:49 fredette Exp >>"; 50 1.4 thorpej #else 51 1.8 christos __RCSID("$NetBSD: ndbootd-bpf.c,v 1.8 2004/12/01 23:18:20 christos Exp $"); 52 1.4 thorpej #endif 53 1.1 fredette 54 1.1 fredette /* includes: */ 55 1.5 mycroft #include <sys/poll.h> 56 1.1 fredette #include <net/bpf.h> 57 1.8 christos #include <paths.h> 58 1.1 fredette 59 1.1 fredette /* structures: */ 60 1.1 fredette struct _ndbootd_interface_bpf { 61 1.1 fredette 62 1.1 fredette /* the size of the packet buffer for the interface: */ 63 1.1 fredette size_t _ndbootd_interface_bpf_buffer_size; 64 1.1 fredette 65 1.1 fredette /* the packet buffer for the interface: */ 66 1.1 fredette char *_ndbootd_interface_bpf_buffer; 67 1.1 fredette 68 1.1 fredette /* the next offset within the packet buffer, and the end of the data 69 1.1 fredette * in the packet buffer: */ 70 1.1 fredette size_t _ndbootd_interface_bpf_buffer_offset; 71 1.1 fredette size_t _ndbootd_interface_bpf_buffer_end; 72 1.1 fredette }; 73 1.1 fredette 74 1.1 fredette /* the BPF program to capture ND packets: */ 75 1.1 fredette static struct bpf_insn ndboot_bpf_filter[] = { 76 1.1 fredette 77 1.1 fredette /* drop this packet if its ethertype isn't ETHERTYPE_IP: */ 78 1.1 fredette BPF_STMT(BPF_LD + BPF_H + BPF_ABS, NDBOOTD_OFFSETOF(struct ether_header, ether_type)), 79 1.1 fredette BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 9), 80 1.1 fredette 81 1.1 fredette /* drop this packet if its IP protocol isn't IPPROTO_ND: */ 82 1.1 fredette BPF_STMT(BPF_LD + BPF_B + BPF_ABS, sizeof(struct ether_header) + NDBOOTD_OFFSETOF(struct ip, ip_p)), 83 1.1 fredette BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_ND, 0, 7), 84 1.1 fredette 85 1.1 fredette /* drop this packet if it's a fragment: */ 86 1.1 fredette BPF_STMT(BPF_LD + BPF_H + BPF_ABS, sizeof(struct ether_header) + NDBOOTD_OFFSETOF(struct ip, ip_off)), 87 1.1 fredette BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x3fff, 5, 0), 88 1.1 fredette 89 1.1 fredette /* drop this packet if it is carrying data (we only want requests, 90 1.1 fredette * which have no data): */ 91 1.1 fredette BPF_STMT(BPF_LD + BPF_H + BPF_ABS, sizeof(struct ether_header) + NDBOOTD_OFFSETOF(struct ip, ip_len)), 92 1.1 fredette BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, sizeof(struct ether_header)), 93 1.1 fredette BPF_STMT(BPF_ALU + BPF_SUB + BPF_X, 0), 94 1.1 fredette BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ndboot_packet), 0, 1), 95 1.1 fredette 96 1.1 fredette /* accept this packet: */ 97 1.1 fredette BPF_STMT(BPF_RET + BPF_K, (u_int) -1), 98 1.1 fredette 99 1.1 fredette /* drop this packet: */ 100 1.1 fredette BPF_STMT(BPF_RET + BPF_K, 0), 101 1.1 fredette }; 102 1.1 fredette 103 1.1 fredette /* this opens a raw socket using BPF. */ 104 1.1 fredette int 105 1.1 fredette ndbootd_raw_open(struct ndbootd_interface * interface) 106 1.1 fredette { 107 1.1 fredette int network_fd; 108 1.1 fredette int saved_errno; 109 1.7 darrenr u_int bufsize; 110 1.1 fredette u_int bpf_opt; 111 1.1 fredette struct bpf_version version; 112 1.1 fredette u_int packet_buffer_size; 113 1.1 fredette struct bpf_program program; 114 1.1 fredette struct _ndbootd_interface_bpf *interface_bpf; 115 1.8 christos const char *dev_bpf_filename = _PATH_BPF; 116 1.1 fredette 117 1.8 christos /* loop trying to open the /dev/bpf device: */ 118 1.8 christos if ((network_fd = open(dev_bpf_filename, O_RDWR)) < 0) { 119 1.8 christos /* we have failed: */ 120 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to open %s: %s", dev_bpf_filename, strerror(errno))); 121 1.1 fredette return (-1); 122 1.1 fredette } 123 1.8 christos _NDBOOTD_DEBUG((fp, "bpf: opened %s", dev_bpf_filename)); 124 1.1 fredette 125 1.1 fredette /* this macro helps in closing the BPF socket on error: */ 126 1.1 fredette #define _NDBOOTD_RAW_OPEN_ERROR(x) saved_errno = errno; x; errno = saved_errno 127 1.1 fredette 128 1.1 fredette /* check the BPF version: */ 129 1.1 fredette if (ioctl(network_fd, BIOCVERSION, &version) < 0) { 130 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to get the BPF version on %s: %s", 131 1.1 fredette dev_bpf_filename, strerror(errno))); 132 1.1 fredette _NDBOOTD_RAW_OPEN_ERROR(close(network_fd)); 133 1.1 fredette return (-1); 134 1.1 fredette } 135 1.1 fredette if (version.bv_major != BPF_MAJOR_VERSION 136 1.1 fredette || version.bv_minor < BPF_MINOR_VERSION) { 137 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: kernel BPF version is %d.%d, my BPF version is %d.%d", 138 1.1 fredette version.bv_major, version.bv_minor, 139 1.1 fredette BPF_MAJOR_VERSION, BPF_MINOR_VERSION)); 140 1.1 fredette close(network_fd); 141 1.1 fredette errno = ENXIO; 142 1.1 fredette return (-1); 143 1.1 fredette } 144 1.1 fredette /* put the BPF device into immediate mode: */ 145 1.1 fredette bpf_opt = TRUE; 146 1.1 fredette if (ioctl(network_fd, BIOCIMMEDIATE, &bpf_opt) < 0) { 147 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to put %s into immediate mode: %s", 148 1.1 fredette dev_bpf_filename, strerror(errno))); 149 1.1 fredette _NDBOOTD_RAW_OPEN_ERROR(close(network_fd)); 150 1.1 fredette return (-1); 151 1.1 fredette } 152 1.7 darrenr /* set a reasonable sized buffer for the BPF device */ 153 1.7 darrenr bufsize = 32768; 154 1.7 darrenr if (ioctl(network_fd, BIOCSBLEN, &bufsize) < 0) { 155 1.7 darrenr _NDBOOTD_DEBUG((fp, "bpf: failed set buffer size to %d: %s", 156 1.7 darrenr bufsize, strerror(errno))); 157 1.7 darrenr } 158 1.1 fredette /* tell the BPF device we're providing complete Ethernet headers: */ 159 1.1 fredette bpf_opt = TRUE; 160 1.1 fredette if (ioctl(network_fd, BIOCSHDRCMPLT, &bpf_opt) < 0) { 161 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to put %s into complete-headers mode: %s", 162 1.1 fredette dev_bpf_filename, strerror(errno))); 163 1.1 fredette _NDBOOTD_RAW_OPEN_ERROR(close(network_fd)); 164 1.1 fredette return (-1); 165 1.1 fredette } 166 1.1 fredette /* point the BPF device at the interface we're using: */ 167 1.1 fredette if (ioctl(network_fd, BIOCSETIF, interface->ndbootd_interface_ifreq) < 0) { 168 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to point BPF socket at %s: %s", 169 1.1 fredette interface->ndbootd_interface_ifreq->ifr_name, strerror(errno))); 170 1.1 fredette saved_errno = errno; 171 1.1 fredette close(network_fd); 172 1.1 fredette errno = saved_errno; 173 1.1 fredette return (-1); 174 1.1 fredette } 175 1.1 fredette /* set the filter on the BPF device: */ 176 1.1 fredette program.bf_len = sizeof(ndboot_bpf_filter) / sizeof(ndboot_bpf_filter[0]); 177 1.1 fredette program.bf_insns = ndboot_bpf_filter; 178 1.1 fredette if (ioctl(network_fd, BIOCSETF, &program) < 0) { 179 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to set the filter on %s: %s", 180 1.1 fredette dev_bpf_filename, strerror(errno))); 181 1.1 fredette _NDBOOTD_RAW_OPEN_ERROR(close(network_fd)); 182 1.1 fredette return (-1); 183 1.1 fredette } 184 1.1 fredette /* get the BPF read buffer size: */ 185 1.1 fredette if (ioctl(network_fd, BIOCGBLEN, &packet_buffer_size) < 0) { 186 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to read the buffer size for %s: %s", 187 1.1 fredette dev_bpf_filename, strerror(errno))); 188 1.1 fredette _NDBOOTD_RAW_OPEN_ERROR(close(network_fd)); 189 1.1 fredette return (-1); 190 1.1 fredette } 191 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: buffer size for %s is %u", 192 1.1 fredette dev_bpf_filename, packet_buffer_size)); 193 1.1 fredette 194 1.1 fredette /* allocate our private interface information and we're done: */ 195 1.1 fredette interface->ndbootd_interface_fd = network_fd; 196 1.1 fredette interface_bpf = ndbootd_new0(struct _ndbootd_interface_bpf, 1); 197 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_size = packet_buffer_size; 198 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer = ndbootd_new(char, packet_buffer_size); 199 1.1 fredette interface->_ndbootd_interface_raw_private = interface_bpf; 200 1.1 fredette return (0); 201 1.1 fredette #undef _NDBOOTD_RAW_OPEN_ERROR 202 1.1 fredette } 203 1.1 fredette 204 1.1 fredette /* this reads a raw packet: */ 205 1.1 fredette int 206 1.1 fredette ndbootd_raw_read(struct ndbootd_interface * interface, void *packet_buffer, size_t packet_buffer_size) 207 1.1 fredette { 208 1.1 fredette struct _ndbootd_interface_bpf *interface_bpf; 209 1.1 fredette ssize_t buffer_end; 210 1.1 fredette struct bpf_hdr the_bpf_header; 211 1.5 mycroft struct pollfd set[1]; 212 1.1 fredette 213 1.1 fredette /* recover our state: */ 214 1.1 fredette interface_bpf = (struct _ndbootd_interface_bpf *) interface->_ndbootd_interface_raw_private; 215 1.1 fredette 216 1.1 fredette /* loop until we have something to return: */ 217 1.5 mycroft set[0].fd = interface->ndbootd_interface_fd; 218 1.5 mycroft set[0].events = POLLIN; 219 1.1 fredette for (;;) { 220 1.1 fredette 221 1.1 fredette /* if the buffer is empty, fill it: */ 222 1.1 fredette if (interface_bpf->_ndbootd_interface_bpf_buffer_offset 223 1.1 fredette >= interface_bpf->_ndbootd_interface_bpf_buffer_end) { 224 1.1 fredette 225 1.5 mycroft /* poll on the BPF socket: */ 226 1.5 mycroft _NDBOOTD_DEBUG((fp, "bpf: calling poll")); 227 1.5 mycroft switch (poll(set, 1, INFTIM)) { 228 1.1 fredette case 0: 229 1.5 mycroft _NDBOOTD_DEBUG((fp, "bpf: poll returned zero")); 230 1.1 fredette continue; 231 1.1 fredette case 1: 232 1.1 fredette break; 233 1.1 fredette default: 234 1.1 fredette if (errno == EINTR) { 235 1.5 mycroft _NDBOOTD_DEBUG((fp, "bpf: poll got EINTR")); 236 1.1 fredette continue; 237 1.1 fredette } 238 1.5 mycroft _NDBOOTD_DEBUG((fp, "bpf: poll failed: %s", strerror(errno))); 239 1.1 fredette return (-1); 240 1.1 fredette } 241 1.5 mycroft assert(set[0].revents & POLLIN); 242 1.1 fredette 243 1.1 fredette /* read the BPF socket: */ 244 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: calling read")); 245 1.1 fredette buffer_end = read(interface->ndbootd_interface_fd, 246 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer, 247 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_size); 248 1.1 fredette if (buffer_end <= 0) { 249 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: failed to read packets: %s", strerror(errno))); 250 1.1 fredette return (-1); 251 1.1 fredette } 252 1.3 fredette _NDBOOTD_DEBUG((fp, "bpf: read %ld bytes of packets", (long) buffer_end)); 253 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset = 0; 254 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_end = buffer_end; 255 1.1 fredette } 256 1.1 fredette /* if there's not enough for a BPF header, flush the buffer: */ 257 1.1 fredette if ((interface_bpf->_ndbootd_interface_bpf_buffer_offset 258 1.1 fredette + sizeof(the_bpf_header)) 259 1.1 fredette > interface_bpf->_ndbootd_interface_bpf_buffer_end) { 260 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: flushed garbage BPF header bytes")); 261 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_end = 0; 262 1.1 fredette continue; 263 1.1 fredette } 264 1.1 fredette /* get the BPF header and check it: */ 265 1.1 fredette memcpy(&the_bpf_header, 266 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer 267 1.1 fredette + interface_bpf->_ndbootd_interface_bpf_buffer_offset, 268 1.1 fredette sizeof(the_bpf_header)); 269 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset += the_bpf_header.bh_hdrlen; 270 1.1 fredette 271 1.1 fredette /* if we're missing some part of the packet: */ 272 1.1 fredette if (the_bpf_header.bh_caplen != the_bpf_header.bh_datalen 273 1.1 fredette || ((interface_bpf->_ndbootd_interface_bpf_buffer_offset + the_bpf_header.bh_datalen) 274 1.1 fredette > interface_bpf->_ndbootd_interface_bpf_buffer_end)) { 275 1.1 fredette _NDBOOTD_DEBUG((fp, "bpf: flushed truncated BPF packet")); 276 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset += the_bpf_header.bh_datalen; 277 1.1 fredette continue; 278 1.1 fredette } 279 1.1 fredette /* silently ignore packets that don't even have Ethernet 280 1.1 fredette * headers, and those packets that we transmitted: */ 281 1.1 fredette if (the_bpf_header.bh_datalen < sizeof(struct ether_header) 282 1.1 fredette || !memcmp(((struct ether_header *) 283 1.1 fredette (interface_bpf->_ndbootd_interface_bpf_buffer 284 1.1 fredette + interface_bpf->_ndbootd_interface_bpf_buffer_offset))->ether_shost, 285 1.1 fredette interface->ndbootd_interface_ether, 286 1.1 fredette ETHER_ADDR_LEN)) { 287 1.1 fredette /* silently ignore packets from us: */ 288 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset += the_bpf_header.bh_datalen; 289 1.1 fredette continue; 290 1.1 fredette } 291 1.1 fredette /* if the caller hasn't provided a large enough buffer: */ 292 1.1 fredette if (packet_buffer_size < the_bpf_header.bh_datalen) { 293 1.1 fredette errno = EIO; 294 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset += the_bpf_header.bh_datalen; 295 1.1 fredette return (-1); 296 1.1 fredette } 297 1.1 fredette /* return this captured packet to the user: */ 298 1.1 fredette memcpy(packet_buffer, 299 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer 300 1.1 fredette + interface_bpf->_ndbootd_interface_bpf_buffer_offset, 301 1.1 fredette the_bpf_header.bh_datalen); 302 1.1 fredette interface_bpf->_ndbootd_interface_bpf_buffer_offset += the_bpf_header.bh_datalen; 303 1.1 fredette return (the_bpf_header.bh_datalen); 304 1.1 fredette } 305 1.1 fredette /* NOTREACHED */ 306 1.1 fredette } 307 1.1 fredette 308 1.1 fredette /* this writes a raw packet: */ 309 1.1 fredette int 310 1.1 fredette ndbootd_raw_write(struct ndbootd_interface * interface, void *packet_buffer, size_t packet_buffer_size) 311 1.1 fredette { 312 1.1 fredette return (write(interface->ndbootd_interface_fd, packet_buffer, packet_buffer_size)); 313 1.1 fredette } 314