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