Index: head/usr.sbin/btxld/btxld.c =================================================================== --- head/usr.sbin/btxld/btxld.c (revision 298215) +++ head/usr.sbin/btxld/btxld.c (revision 298216) @@ -1,575 +1,573 @@ /* * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include /* XXX make this work as an i386/amd64 cross-tool */ #include #undef __LDPGSZ #define __LDPGSZ 4096 #include #include #include #include #include #include #include #include #include #include #include "btx.h" #include "elfh.h" #define BTX_PATH "/sys/boot/i386/btx" #define I_LDR 0 /* BTX loader */ #define I_BTX 1 /* BTX kernel */ #define I_CLNT 2 /* Client program */ #define F_BIN 0 /* Binary */ #define F_AOUT 1 /* ZMAGIC a.out */ #define F_ELF 2 /* 32-bit ELF */ #define F_CNT 3 /* Number of formats */ #define IMPURE 1 /* Writable text */ #define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */ -#define align(x, y) (((x) + (y) - 1) & ~((y) - 1)) - struct hdr { uint32_t fmt; /* Format */ uint32_t flags; /* Bit flags */ uint32_t size; /* Size of file */ uint32_t text; /* Size of text segment */ uint32_t data; /* Size of data segment */ uint32_t bss; /* Size of bss segment */ uint32_t org; /* Program origin */ uint32_t entry; /* Program entry point */ }; static const char *const fmtlist[] = {"bin", "aout", "elf"}; static const char binfo[] = "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM " "pgctl=%x:%x\n"; static const char cinfo[] = "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n"; static const char oinfo[] = "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n"; static const char *lname = BTX_PATH "/btxldr/btxldr"; /* BTX loader */ static const char *bname = BTX_PATH "/btx/btx"; /* BTX kernel */ static const char *oname = "a.out"; /* Output filename */ static int ppage = -1; /* First page present */ static int wpage = -1; /* First page writable */ static unsigned int format; /* Output format */ static uint32_t centry; /* Client entry address */ static uint32_t lentry; /* Loader entry address */ static int Eflag; /* Client entry option */ static int quiet; /* Inhibit warnings */ static int verbose; /* Display information */ static const char *tname; /* Temporary output file */ static const char *fname; /* Current input file */ static void cleanup(void); static void btxld(const char *); static void getbtx(int, struct btx_hdr *); static void gethdr(int, struct hdr *); static void puthdr(int, struct hdr *); static void copy(int, int, size_t, off_t); static size_t readx(int, void *, size_t, off_t); static void writex(int, const void *, size_t); static void seekx(int, off_t); static unsigned int optfmt(const char *); static uint32_t optaddr(const char *); static int optpage(const char *, int); static void Warn(const char *, const char *, ...); static void usage(void); /* * A link editor for BTX clients. */ int main(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1) switch (c) { case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'b': bname = optarg; break; case 'E': centry = optaddr(optarg); Eflag = 1; break; case 'e': lentry = optaddr(optarg); break; case 'f': format = optfmt(optarg); break; case 'l': lname = optarg; break; case 'o': oname = optarg; break; case 'P': ppage = optpage(optarg, 1); break; case 'W': wpage = optpage(optarg, BTX_MAXCWR); break; default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); atexit(cleanup); btxld(*argv); return 0; } /* * Clean up after errors. */ static void cleanup(void) { if (tname) remove(tname); } /* * Read the input files; write the output file; display information. */ static void btxld(const char *iname) { char name[FILENAME_MAX]; struct btx_hdr btx, btxle; struct hdr ihdr, ohdr; unsigned int ldr_size, cwr; int fdi[3], fdo, i; ldr_size = 0; for (i = I_LDR; i <= I_CLNT; i++) { fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; if ((fdi[i] = open(fname, O_RDONLY)) == -1) err(2, "%s", fname); switch (i) { case I_LDR: gethdr(fdi[i], &ihdr); if (ihdr.fmt != F_BIN) Warn(fname, "Loader format is %s; processing as %s", fmtlist[ihdr.fmt], fmtlist[F_BIN]); ldr_size = ihdr.size; break; case I_BTX: getbtx(fdi[i], &btx); break; case I_CLNT: gethdr(fdi[i], &ihdr); if (ihdr.org && ihdr.org != BTX_PGSIZE) Warn(fname, "Client origin is 0x%x; expecting 0 or 0x%x", ihdr.org, BTX_PGSIZE); } } memset(&ohdr, 0, sizeof(ohdr)); ohdr.fmt = format; ohdr.text = ldr_size; ohdr.data = btx.btx_textsz + ihdr.size; ohdr.org = lentry; ohdr.entry = lentry; cwr = 0; if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) { if (wpage > 0) cwr = wpage; else { cwr = howmany(ihdr.text, BTX_PGSIZE); if (cwr > BTX_MAXCWR) cwr = BTX_MAXCWR; } } if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) { btx.btx_flags |= BTX_MAPONE; if (!cwr) cwr++; } btx.btx_pgctl -= cwr; btx.btx_entry = Eflag ? centry : ihdr.entry; if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name)) errx(2, "%s: Filename too long", oname); if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1) err(2, "%s", name); if (!(tname = strdup(name))) err(2, NULL); puthdr(fdo, &ohdr); for (i = I_LDR; i <= I_CLNT; i++) { fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; switch (i) { case I_LDR: copy(fdi[i], fdo, ldr_size, 0); seekx(fdo, ohdr.size += ohdr.text); break; case I_BTX: btxle = btx; btxle.btx_pgctl = htole16(btxle.btx_pgctl); btxle.btx_textsz = htole16(btxle.btx_textsz); btxle.btx_entry = htole32(btxle.btx_entry); writex(fdo, &btxle, sizeof(btxle)); copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx), sizeof(btx)); break; case I_CLNT: copy(fdi[i], fdo, ihdr.size, 0); if (ftruncate(fdo, ohdr.size += ohdr.data)) err(2, "%s", tname); } if (close(fdi[i])) err(2, "%s", fname); } if (close(fdo)) err(2, "%s", tname); if (rename(tname, oname)) err(2, "%s: Can't rename to %s", tname, oname); tname = NULL; if (verbose) { printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz, BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) * BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE), BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE / BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE); printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text, ihdr.data, ihdr.bss, ihdr.entry); printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text, ohdr.data, ohdr.org, ohdr.entry); } } /* * Read BTX file header. */ static void getbtx(int fd, struct btx_hdr * btx) { if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) || btx->btx_magic[0] != BTX_MAG0 || btx->btx_magic[1] != BTX_MAG1 || btx->btx_magic[2] != BTX_MAG2) errx(1, "%s: Not a BTX kernel", fname); btx->btx_pgctl = le16toh(btx->btx_pgctl); btx->btx_textsz = le16toh(btx->btx_textsz); btx->btx_entry = le32toh(btx->btx_entry); } /* * Get file size and read a.out or ELF header. */ static void gethdr(int fd, struct hdr *hdr) { struct stat sb; const struct exec *ex; const Elf32_Ehdr *ee; const Elf32_Phdr *ep; void *p; unsigned int fmt, x, n, i; memset(hdr, 0, sizeof(*hdr)); if (fstat(fd, &sb)) err(2, "%s", fname); if (sb.st_size > MAXU32) errx(1, "%s: Too big", fname); hdr->size = sb.st_size; if (!hdr->size) return; if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) err(2, "%s", fname); for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--) switch (fmt) { case F_AOUT: ex = p; if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) { hdr->fmt = fmt; x = N_GETMAGIC(*ex); if (x == OMAGIC || x == NMAGIC) { if (x == NMAGIC) Warn(fname, "Treating %s NMAGIC as OMAGIC", fmtlist[fmt]); hdr->flags |= IMPURE; } hdr->text = le32toh(ex->a_text); hdr->data = le32toh(ex->a_data); hdr->bss = le32toh(ex->a_bss); hdr->entry = le32toh(ex->a_entry); if (le32toh(ex->a_entry) >= BTX_PGSIZE) hdr->org = BTX_PGSIZE; } break; case F_ELF: ee = p; if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) { hdr->fmt = fmt; for (n = i = 0; i < le16toh(ee->e_phnum); i++) { ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) + le16toh(ee->e_phentsize) * i); if (le32toh(ep->p_type) == PT_LOAD) switch (n++) { case 0: hdr->text = le32toh(ep->p_filesz); hdr->org = le32toh(ep->p_paddr); if (le32toh(ep->p_flags) & PF_W) hdr->flags |= IMPURE; break; case 1: hdr->data = le32toh(ep->p_filesz); hdr->bss = le32toh(ep->p_memsz) - le32toh(ep->p_filesz); break; case 2: Warn(fname, "Ignoring extra %s PT_LOAD segments", fmtlist[fmt]); } } hdr->entry = le32toh(ee->e_entry); } } if (munmap(p, hdr->size)) err(2, "%s", fname); } /* * Write a.out or ELF header. */ static void puthdr(int fd, struct hdr *hdr) { struct exec ex; struct elfh eh; switch (hdr->fmt) { case F_AOUT: memset(&ex, 0, sizeof(ex)); N_SETMAGIC(ex, ZMAGIC, MID_I386, 0); hdr->text = N_ALIGN(ex, hdr->text); ex.a_text = htole32(hdr->text); hdr->data = N_ALIGN(ex, hdr->data); ex.a_data = htole32(hdr->data); ex.a_entry = htole32(hdr->entry); writex(fd, &ex, sizeof(ex)); hdr->size = N_ALIGN(ex, sizeof(ex)); seekx(fd, hdr->size); break; case F_ELF: eh = elfhdr; eh.e.e_entry = htole32(hdr->entry); eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org); eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text); eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) + le32toh(eh.p[0].p_filesz)); eh.p[1].p_vaddr = eh.p[1].p_paddr = - htole32(align(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz), + htole32(roundup2(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz), 4096)); eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data); eh.sh[2].sh_addr = eh.p[0].p_vaddr; eh.sh[2].sh_offset = eh.p[0].p_offset; eh.sh[2].sh_size = eh.p[0].p_filesz; eh.sh[3].sh_addr = eh.p[1].p_vaddr; eh.sh[3].sh_offset = eh.p[1].p_offset; eh.sh[3].sh_size = eh.p[1].p_filesz; writex(fd, &eh, sizeof(eh)); hdr->size = sizeof(eh); } } /* * Safe copy from input file to output file. */ static void copy(int fdi, int fdo, size_t nbyte, off_t offset) { char buf[8192]; size_t n; while (nbyte) { if ((n = sizeof(buf)) > nbyte) n = nbyte; if (readx(fdi, buf, n, offset) != n) errx(2, "%s: Short read", fname); writex(fdo, buf, n); nbyte -= n; offset = -1; } } /* * Safe read from input file. */ static size_t readx(int fd, void *buf, size_t nbyte, off_t offset) { ssize_t n; if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset) err(2, "%s", fname); if ((n = read(fd, buf, nbyte)) == -1) err(2, "%s", fname); return n; } /* * Safe write to output file. */ static void writex(int fd, const void *buf, size_t nbyte) { ssize_t n; if ((n = write(fd, buf, nbyte)) == -1) err(2, "%s", tname); if ((size_t)n != nbyte) errx(2, "%s: Short write", tname); } /* * Safe seek in output file. */ static void seekx(int fd, off_t offset) { if (lseek(fd, offset, SEEK_SET) != offset) err(2, "%s", tname); } /* * Convert an option argument to a format code. */ static unsigned int optfmt(const char *arg) { unsigned int i; for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++); if (i == F_CNT) errx(1, "%s: Unknown format", arg); return i; } /* * Convert an option argument to an address. */ static uint32_t optaddr(const char *arg) { char *s; unsigned long x; errno = 0; x = strtoul(arg, &s, 0); if (errno || !*arg || *s || x > MAXU32) errx(1, "%s: Illegal address", arg); return x; } /* * Convert an option argument to a page number. */ static int optpage(const char *arg, int hi) { char *s; long x; errno = 0; x = strtol(arg, &s, 0); if (errno || !*arg || *s || x < 0 || x > hi) errx(1, "%s: Illegal page number", arg); return x; } /* * Display a warning. */ static void Warn(const char *locus, const char *fmt, ...) { va_list ap; char *s; if (!quiet) { asprintf(&s, "%s: Warning: %s", locus, fmt); va_start(ap, fmt); vwarnx(s, ap); va_end(ap); free(s); } } /* * Display usage information. */ static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]", " [-l file] [-o filename] [-P page] [-W page] file"); exit(1); } Index: head/usr.sbin/usbdump/usbdump.c =================================================================== --- head/usr.sbin/usbdump/usbdump.c (revision 298215) +++ head/usr.sbin/usbdump/usbdump.c (revision 298216) @@ -1,986 +1,984 @@ /*- * Copyright (c) 2010 Weongyo Jeong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BPF_STORE_JUMP(x,_c,_k,_jt,_jf) do { \ (x).code = (_c); \ (x).k = (_k); \ (x).jt = (_jt); \ (x).jf = (_jf); \ } while (0) #define BPF_STORE_STMT(x,_c,_k) do { \ (x).code = (_c); \ (x).k = (_k); \ (x).jt = 0; \ (x).jf = 0; \ } while (0) struct usb_filt { STAILQ_ENTRY(usb_filt) entry; int unit; int endpoint; }; struct usbcap { int fd; /* fd for /dev/usbpf */ uint32_t bufsize; uint8_t *buffer; /* for -w option */ int wfd; /* for -r option */ int rfd; /* for -b option */ int bfd; }; struct usbcap_filehdr { uint32_t magic; #define USBCAP_FILEHDR_MAGIC 0x9a90000e uint8_t major; uint8_t minor; uint8_t reserved[26]; } __packed; -#define HEADER_ALIGN(x,a) (((x) + (a) - 1) & ~((a) - 1)) - struct header_32 { /* capture timestamp */ uint32_t ts_sec; uint32_t ts_usec; /* data length and alignment information */ uint32_t caplen; uint32_t datalen; uint8_t hdrlen; uint8_t align; } __packed; static int doexit = 0; static int pkt_captured = 0; static int verbose = 0; static int uf_minor; static const char *i_arg = "usbus0"; static const char *r_arg = NULL; static const char *w_arg = NULL; static const char *b_arg = NULL; static struct usbcap uc; static const char *errstr_table[USB_ERR_MAX] = { [USB_ERR_NORMAL_COMPLETION] = "0", [USB_ERR_PENDING_REQUESTS] = "PENDING_REQUESTS", [USB_ERR_NOT_STARTED] = "NOT_STARTED", [USB_ERR_INVAL] = "INVAL", [USB_ERR_NOMEM] = "NOMEM", [USB_ERR_CANCELLED] = "CANCELLED", [USB_ERR_BAD_ADDRESS] = "BAD_ADDRESS", [USB_ERR_BAD_BUFSIZE] = "BAD_BUFSIZE", [USB_ERR_BAD_FLAG] = "BAD_FLAG", [USB_ERR_NO_CALLBACK] = "NO_CALLBACK", [USB_ERR_IN_USE] = "IN_USE", [USB_ERR_NO_ADDR] = "NO_ADDR", [USB_ERR_NO_PIPE] = "NO_PIPE", [USB_ERR_ZERO_NFRAMES] = "ZERO_NFRAMES", [USB_ERR_ZERO_MAXP] = "ZERO_MAXP", [USB_ERR_SET_ADDR_FAILED] = "SET_ADDR_FAILED", [USB_ERR_NO_POWER] = "NO_POWER", [USB_ERR_TOO_DEEP] = "TOO_DEEP", [USB_ERR_IOERROR] = "IOERROR", [USB_ERR_NOT_CONFIGURED] = "NOT_CONFIGURED", [USB_ERR_TIMEOUT] = "TIMEOUT", [USB_ERR_SHORT_XFER] = "SHORT_XFER", [USB_ERR_STALLED] = "STALLED", [USB_ERR_INTERRUPTED] = "INTERRUPTED", [USB_ERR_DMA_LOAD_FAILED] = "DMA_LOAD_FAILED", [USB_ERR_BAD_CONTEXT] = "BAD_CONTEXT", [USB_ERR_NO_ROOT_HUB] = "NO_ROOT_HUB", [USB_ERR_NO_INTR_THREAD] = "NO_INTR_THREAD", [USB_ERR_NOT_LOCKED] = "NOT_LOCKED", }; static const char *xfertype_table[4] = { [UE_CONTROL] = "CTRL", [UE_ISOCHRONOUS] = "ISOC", [UE_BULK] = "BULK", [UE_INTERRUPT] = "INTR" }; static const char *speed_table[USB_SPEED_MAX] = { [USB_SPEED_FULL] = "FULL", [USB_SPEED_HIGH] = "HIGH", [USB_SPEED_LOW] = "LOW", [USB_SPEED_VARIABLE] = "VARI", [USB_SPEED_SUPER] = "SUPER", }; static STAILQ_HEAD(,usb_filt) usb_filt_head = STAILQ_HEAD_INITIALIZER(usb_filt_head); static void add_filter(int usb_filt_unit, int usb_filt_ep) { struct usb_filt *puf; puf = malloc(sizeof(struct usb_filt)); if (puf == NULL) errx(EX_SOFTWARE, "Out of memory."); puf->unit = usb_filt_unit; puf->endpoint = usb_filt_ep; STAILQ_INSERT_TAIL(&usb_filt_head, puf, entry); } static void make_filter(struct bpf_program *pprog, int snapshot) { struct usb_filt *puf; struct bpf_insn *dynamic_insn; int len; len = 0; STAILQ_FOREACH(puf, &usb_filt_head, entry) len++; dynamic_insn = malloc(((len * 5) + 1) * sizeof(struct bpf_insn)); if (dynamic_insn == NULL) errx(EX_SOFTWARE, "Out of memory."); len++; if (len == 1) { /* accept all packets */ BPF_STORE_STMT(dynamic_insn[0], BPF_RET | BPF_K, snapshot); goto done; } len = 0; STAILQ_FOREACH(puf, &usb_filt_head, entry) { const int addr_off = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_address; const int addr_ep = (uintptr_t)&((struct usbpf_pkthdr *)0)->up_endpoint; if (puf->unit != -1) { if (puf->endpoint != -1) { BPF_STORE_STMT(dynamic_insn[len], BPF_LD | BPF_B | BPF_ABS, addr_off); len++; BPF_STORE_JUMP(dynamic_insn[len], BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 3); len++; BPF_STORE_STMT(dynamic_insn[len], BPF_LD | BPF_W | BPF_ABS, addr_ep); len++; BPF_STORE_JUMP(dynamic_insn[len], BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); len++; } else { BPF_STORE_STMT(dynamic_insn[len], BPF_LD | BPF_B | BPF_ABS, addr_off); len++; BPF_STORE_JUMP(dynamic_insn[len], BPF_JMP | BPF_JEQ | BPF_K, (uint8_t)puf->unit, 0, 1); len++; } } else { if (puf->endpoint != -1) { BPF_STORE_STMT(dynamic_insn[len], BPF_LD | BPF_W | BPF_ABS, addr_ep); len++; BPF_STORE_JUMP(dynamic_insn[len], BPF_JMP | BPF_JEQ | BPF_K, htobe32(puf->endpoint), 0, 1); len++; } } BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, snapshot); len++; } BPF_STORE_STMT(dynamic_insn[len], BPF_RET | BPF_K, 0); len++; done: pprog->bf_len = len; pprog->bf_insns = dynamic_insn; } static int match_filter(int unit, int endpoint) { struct usb_filt *puf; if (STAILQ_FIRST(&usb_filt_head) == NULL) return (1); STAILQ_FOREACH(puf, &usb_filt_head, entry) { if ((puf->unit == -1 || puf->unit == unit) && (puf->endpoint == -1 || puf->endpoint == endpoint)) return (1); } return (0); } static void free_filter(struct bpf_program *pprog) { struct usb_filt *puf; while ((puf = STAILQ_FIRST(&usb_filt_head)) != NULL) { STAILQ_REMOVE_HEAD(&usb_filt_head, entry); free(puf); } free(pprog->bf_insns); } static void handle_sigint(int sig) { (void)sig; doexit = 1; } #define FLAGS(x, name) \ (((x) & USBPF_FLAG_##name) ? #name "|" : "") #define STATUS(x, name) \ (((x) & USBPF_STATUS_##name) ? #name "|" : "") static const char * usb_errstr(uint32_t error) { if (error >= USB_ERR_MAX || errstr_table[error] == NULL) return ("UNKNOWN"); else return (errstr_table[error]); } static const char * usb_speedstr(uint8_t speed) { if (speed >= USB_SPEED_MAX || speed_table[speed] == NULL) return ("UNKNOWN"); else return (speed_table[speed]); } static void print_flags(uint32_t flags) { printf(" flags %#x <%s%s%s%s%s%s%s%s%s0>\n", flags, FLAGS(flags, FORCE_SHORT_XFER), FLAGS(flags, SHORT_XFER_OK), FLAGS(flags, SHORT_FRAMES_OK), FLAGS(flags, PIPE_BOF), FLAGS(flags, PROXY_BUFFER), FLAGS(flags, EXT_BUFFER), FLAGS(flags, MANUAL_STATUS), FLAGS(flags, NO_PIPE_OK), FLAGS(flags, STALL_PIPE)); } static void print_status(uint32_t status) { printf(" status %#x <%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s0>\n", status, STATUS(status, OPEN), STATUS(status, TRANSFERRING), STATUS(status, DID_DMA_DELAY), STATUS(status, DID_CLOSE), STATUS(status, DRAINING), STATUS(status, STARTED), STATUS(status, BW_RECLAIMED), STATUS(status, CONTROL_XFR), STATUS(status, CONTROL_HDR), STATUS(status, CONTROL_ACT), STATUS(status, CONTROL_STALL), STATUS(status, SHORT_FRAMES_OK), STATUS(status, SHORT_XFER_OK), STATUS(status, BDMA_ENABLE), STATUS(status, BDMA_NO_POST_SYNC), STATUS(status, BDMA_SETUP), STATUS(status, ISOCHRONOUS_XFR), STATUS(status, CURR_DMA_SET), STATUS(status, CAN_CANCEL_IMMED), STATUS(status, DOING_CALLBACK)); } /* * Dump a byte into hex format. */ static void hexbyte(char *buf, uint8_t temp) { uint8_t lo; uint8_t hi; lo = temp & 0xF; hi = temp >> 4; if (hi < 10) buf[0] = '0' + hi; else buf[0] = 'A' + hi - 10; if (lo < 10) buf[1] = '0' + lo; else buf[1] = 'A' + lo - 10; } /* * Display a region in traditional hexdump format. */ static void hexdump(const uint8_t *region, uint32_t len) { const uint8_t *line; char linebuf[128]; int i; int x; int c; for (line = region; line < (region + len); line += 16) { i = 0; linebuf[i] = ' '; hexbyte(linebuf + i + 1, ((line - region) >> 8) & 0xFF); hexbyte(linebuf + i + 3, (line - region) & 0xFF); linebuf[i + 5] = ' '; linebuf[i + 6] = ' '; i += 7; for (x = 0; x < 16; x++) { if ((line + x) < (region + len)) { hexbyte(linebuf + i, *(const u_int8_t *)(line + x)); } else { linebuf[i] = '-'; linebuf[i + 1] = '-'; } linebuf[i + 2] = ' '; if (x == 7) { linebuf[i + 3] = ' '; i += 4; } else { i += 3; } } linebuf[i] = ' '; linebuf[i + 1] = '|'; i += 2; for (x = 0; x < 16; x++) { if ((line + x) < (region + len)) { c = *(const u_int8_t *)(line + x); /* !isprint(c) */ if ((c < ' ') || (c > '~')) c = '.'; linebuf[i] = c; } else { linebuf[i] = ' '; } i++; } linebuf[i] = '|'; linebuf[i + 1] = 0; i += 2; puts(linebuf); } } static void print_apacket(const struct header_32 *hdr, const uint8_t *ptr, int ptr_len) { struct tm *tm; struct usbpf_pkthdr up_temp; struct usbpf_pkthdr *up; struct timeval tv; size_t len; uint32_t x; char buf[64]; ptr += USBPF_HDR_LEN; ptr_len -= USBPF_HDR_LEN; if (ptr_len < 0) return; /* make sure we don't change the source buffer */ memcpy(&up_temp, ptr - USBPF_HDR_LEN, sizeof(up_temp)); up = &up_temp; /* * A packet from the kernel is based on little endian byte * order. */ up->up_totlen = le32toh(up->up_totlen); up->up_busunit = le32toh(up->up_busunit); up->up_flags = le32toh(up->up_flags); up->up_status = le32toh(up->up_status); up->up_error = le32toh(up->up_error); up->up_interval = le32toh(up->up_interval); up->up_frames = le32toh(up->up_frames); up->up_packet_size = le32toh(up->up_packet_size); up->up_packet_count = le32toh(up->up_packet_count); up->up_endpoint = le32toh(up->up_endpoint); if (!match_filter(up->up_address, up->up_endpoint)) return; tv.tv_sec = hdr->ts_sec; tv.tv_usec = hdr->ts_usec; tm = localtime(&tv.tv_sec); len = strftime(buf, sizeof(buf), "%H:%M:%S", tm); if (verbose >= 0) { printf("%.*s.%06ld usbus%d.%d %s-%s-EP=%08x,SPD=%s,NFR=%d,SLEN=%d,IVAL=%d%s%s\n", (int)len, buf, tv.tv_usec, (int)up->up_busunit, (int)up->up_address, (up->up_type == USBPF_XFERTAP_SUBMIT) ? "SUBM" : "DONE", xfertype_table[up->up_xfertype], (unsigned int)up->up_endpoint, usb_speedstr(up->up_speed), (int)up->up_frames, (int)(up->up_totlen - USBPF_HDR_LEN - (USBPF_FRAME_HDR_LEN * up->up_frames)), (int)up->up_interval, (up->up_type == USBPF_XFERTAP_DONE) ? ",ERR=" : "", (up->up_type == USBPF_XFERTAP_DONE) ? usb_errstr(up->up_error) : ""); } if (verbose >= 1 || b_arg != NULL) { for (x = 0; x != up->up_frames; x++) { const struct usbpf_framehdr *uf; uint32_t framelen; uint32_t flags; uf = (const struct usbpf_framehdr *)ptr; ptr += USBPF_FRAME_HDR_LEN; ptr_len -= USBPF_FRAME_HDR_LEN; if (ptr_len < 0) return; framelen = le32toh(uf->length); flags = le32toh(uf->flags); if (verbose >= 1) { printf(" frame[%u] %s %d bytes\n", (unsigned int)x, (flags & USBPF_FRAMEFLAG_READ) ? "READ" : "WRITE", (int)framelen); } if (flags & USBPF_FRAMEFLAG_DATA_FOLLOWS) { int tot_frame_len; tot_frame_len = USBPF_FRAME_ALIGN(framelen); ptr_len -= tot_frame_len; if (tot_frame_len < 0 || (int)framelen < 0 || (int)ptr_len < 0) break; if (b_arg != NULL) { struct usbcap *p = &uc; int ret; ret = write(p->bfd, ptr, framelen); if (ret != (int)framelen) err(EXIT_FAILURE, "Could not write binary data"); } if (verbose >= 1) hexdump(ptr, framelen); ptr += tot_frame_len; } } } if (verbose >= 2) print_flags(up->up_flags); if (verbose >= 3) print_status(up->up_status); } static void fix_packets(uint8_t *data, const int datalen) { struct header_32 temp; uint8_t *ptr; uint8_t *next; uint32_t hdrlen; uint32_t caplen; for (ptr = data; ptr < (data + datalen); ptr = next) { const struct bpf_hdr *hdr; hdr = (const struct bpf_hdr *)ptr; temp.ts_sec = htole32(hdr->bh_tstamp.tv_sec); temp.ts_usec = htole32(hdr->bh_tstamp.tv_usec); temp.caplen = htole32(hdr->bh_caplen); temp.datalen = htole32(hdr->bh_datalen); temp.hdrlen = hdr->bh_hdrlen; temp.align = BPF_WORDALIGN(1); hdrlen = hdr->bh_hdrlen; caplen = hdr->bh_caplen; if ((hdrlen >= sizeof(temp)) && (hdrlen <= 255) && ((ptr + hdrlen) <= (data + datalen))) { memcpy(ptr, &temp, sizeof(temp)); memset(ptr + sizeof(temp), 0, hdrlen - sizeof(temp)); } else { err(EXIT_FAILURE, "Invalid header length %d", hdrlen); } next = ptr + BPF_WORDALIGN(hdrlen + caplen); if (next <= ptr) err(EXIT_FAILURE, "Invalid length"); } } static void print_packets(uint8_t *data, const int datalen) { struct header_32 temp; uint8_t *ptr; uint8_t *next; for (ptr = data; ptr < (data + datalen); ptr = next) { const struct header_32 *hdr32; hdr32 = (const struct header_32 *)ptr; temp.ts_sec = le32toh(hdr32->ts_sec); temp.ts_usec = le32toh(hdr32->ts_usec); temp.caplen = le32toh(hdr32->caplen); temp.datalen = le32toh(hdr32->datalen); temp.hdrlen = hdr32->hdrlen; temp.align = hdr32->align; - next = ptr + HEADER_ALIGN(temp.hdrlen + temp.caplen, temp.align); + next = ptr + roundup2(temp.hdrlen + temp.caplen, temp.align); if (next <= ptr) err(EXIT_FAILURE, "Invalid length"); if (verbose >= 0 || r_arg != NULL || b_arg != NULL) { print_apacket(&temp, ptr + temp.hdrlen, temp.caplen); } pkt_captured++; } } static void write_packets(struct usbcap *p, const uint8_t *data, const int datalen) { int len = htole32(datalen); int ret; ret = write(p->wfd, &len, sizeof(int)); if (ret != sizeof(int)) { err(EXIT_FAILURE, "Could not write length " "field of USB data payload"); } ret = write(p->wfd, data, datalen); if (ret != datalen) { err(EXIT_FAILURE, "Could not write " "complete USB data payload"); } } static void read_file(struct usbcap *p) { int datalen; int ret; uint8_t *data; while ((ret = read(p->rfd, &datalen, sizeof(int))) == sizeof(int)) { datalen = le32toh(datalen); data = malloc(datalen); if (data == NULL) errx(EX_SOFTWARE, "Out of memory."); ret = read(p->rfd, data, datalen); if (ret != datalen) { err(EXIT_FAILURE, "Could not read complete " "USB data payload"); } if (uf_minor == 2) fix_packets(data, datalen); print_packets(data, datalen); free(data); } } static void do_loop(struct usbcap *p) { int cc; while (doexit == 0) { cc = read(p->fd, (uint8_t *)p->buffer, p->bufsize); if (cc < 0) { switch (errno) { case EINTR: break; default: fprintf(stderr, "read: %s\n", strerror(errno)); return; } continue; } if (cc == 0) continue; fix_packets(p->buffer, cc); if (w_arg != NULL) write_packets(p, p->buffer, cc); print_packets(p->buffer, cc); } } static void init_rfile(struct usbcap *p) { struct usbcap_filehdr uf; int ret; p->rfd = open(r_arg, O_RDONLY); if (p->rfd < 0) { err(EXIT_FAILURE, "Could not open " "'%s' for read", r_arg); } ret = read(p->rfd, &uf, sizeof(uf)); if (ret != sizeof(uf)) { err(EXIT_FAILURE, "Could not read USB capture " "file header"); } if (le32toh(uf.magic) != USBCAP_FILEHDR_MAGIC) { errx(EX_SOFTWARE, "Invalid magic field(0x%08x) " "in USB capture file header.", (unsigned int)le32toh(uf.magic)); } if (uf.major != 0) { errx(EX_SOFTWARE, "Invalid major version(%d) " "field in USB capture file header.", (int)uf.major); } uf_minor = uf.minor; if (uf.minor != 3 && uf.minor != 2) { errx(EX_SOFTWARE, "Invalid minor version(%d) " "field in USB capture file header.", (int)uf.minor); } } static void init_wfile(struct usbcap *p) { struct usbcap_filehdr uf; int ret; p->wfd = open(w_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); if (p->wfd < 0) { err(EXIT_FAILURE, "Could not open " "'%s' for write", w_arg); } memset(&uf, 0, sizeof(uf)); uf.magic = htole32(USBCAP_FILEHDR_MAGIC); uf.major = 0; uf.minor = 3; ret = write(p->wfd, (const void *)&uf, sizeof(uf)); if (ret != sizeof(uf)) { err(EXIT_FAILURE, "Could not write " "USB capture header"); } } static void usage(void) { #define FMT " %-14s %s\n" fprintf(stderr, "usage: usbdump [options]\n"); fprintf(stderr, FMT, "-i ", "Listen on USB bus interface"); fprintf(stderr, FMT, "-f ", "Specify a device and endpoint filter"); fprintf(stderr, FMT, "-r ", "Read the raw packets from file"); fprintf(stderr, FMT, "-s ", "Snapshot bytes from each packet"); fprintf(stderr, FMT, "-v", "Increase the verbose level"); fprintf(stderr, FMT, "-b ", "Save raw version of all recorded data to file"); fprintf(stderr, FMT, "-w ", "Write the raw packets to file"); fprintf(stderr, FMT, "-h", "Display summary of command line options"); #undef FMT exit(EX_USAGE); } static void check_usb_pf_sysctl(void) { int error; int no_pf_val = 0; size_t no_pf_len = sizeof(int); /* check "hw.usb.no_pf" sysctl for 8- and 9- stable */ error = sysctlbyname("hw.usb.no_pf", &no_pf_val, &no_pf_len, NULL, 0); if (error == 0 && no_pf_val != 0) { warnx("The USB packet filter might be disabled."); warnx("See the \"hw.usb.no_pf\" sysctl for more information."); } } int main(int argc, char *argv[]) { struct timeval tv; struct bpf_program total_prog; struct bpf_stat us; struct bpf_version bv; struct usbcap *p = &uc; struct ifreq ifr; long snapshot = 192; uint32_t v; int fd; int o; int filt_unit; int filt_ep; int s; int ifindex; const char *optstring; char *pp; optstring = "b:hi:r:s:vw:f:"; while ((o = getopt(argc, argv, optstring)) != -1) { switch (o) { case 'i': i_arg = optarg; break; case 'r': r_arg = optarg; init_rfile(p); break; case 's': snapshot = strtol(optarg, &pp, 10); errno = 0; if (pp != NULL && *pp != 0) usage(); if (snapshot == 0 && errno == EINVAL) usage(); /* snapeshot == 0 is special */ if (snapshot == 0) snapshot = -1; break; case 'b': b_arg = optarg; break; case 'v': verbose++; break; case 'w': w_arg = optarg; init_wfile(p); break; case 'f': filt_unit = strtol(optarg, &pp, 10); filt_ep = -1; if (pp != NULL) { if (*pp == '.') { filt_ep = strtol(pp + 1, &pp, 10); if (pp != NULL && *pp != 0) usage(); } else if (*pp != 0) { usage(); } } add_filter(filt_unit, filt_ep); break; default: usage(); /* NOTREACHED */ } } if (b_arg != NULL) { p->bfd = open(b_arg, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); if (p->bfd < 0) { err(EXIT_FAILURE, "Could not open " "'%s' for write", b_arg); } } /* * Require more verbosity to print anything when -w or -b is * specified on the command line: */ if (w_arg != NULL || b_arg != NULL) verbose--; if (r_arg != NULL) { read_file(p); exit(EXIT_SUCCESS); } check_usb_pf_sysctl(); p->fd = fd = open("/dev/bpf", O_RDONLY); if (p->fd < 0) err(EXIT_FAILURE, "Could not open BPF device"); if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) err(EXIT_FAILURE, "BIOCVERSION ioctl failed"); if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) errx(EXIT_FAILURE, "Kernel BPF filter out of date"); /* USB transfers can be greater than 64KByte */ v = 1U << 16; /* clear ifr structure */ memset(&ifr, 0, sizeof(ifr)); /* Try to create usbusN interface if it is not available. */ s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s < 0) errx(EXIT_FAILURE, "Could not open a socket"); ifindex = if_nametoindex(i_arg); if (ifindex == 0) { (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) errx(EXIT_FAILURE, "Invalid bus interface: %s", i_arg); } for ( ; v >= USBPF_HDR_LEN; v >>= 1) { (void)ioctl(fd, BIOCSBLEN, (caddr_t)&v); (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) break; } if (v == 0) errx(EXIT_FAILURE, "No buffer size worked."); if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) err(EXIT_FAILURE, "BIOCGBLEN ioctl failed"); p->bufsize = v; p->buffer = (uint8_t *)malloc(p->bufsize); if (p->buffer == NULL) errx(EX_SOFTWARE, "Out of memory."); make_filter(&total_prog, snapshot); if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) err(EXIT_FAILURE, "BIOCSETF ioctl failed"); free_filter(&total_prog); /* 1 second read timeout */ tv.tv_sec = 1; tv.tv_usec = 0; if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&tv) < 0) err(EXIT_FAILURE, "BIOCSRTIMEOUT ioctl failed"); (void)signal(SIGINT, handle_sigint); do_loop(p); if (ioctl(fd, BIOCGSTATS, (caddr_t)&us) < 0) err(EXIT_FAILURE, "BIOCGSTATS ioctl failed"); /* XXX what's difference between pkt_captured and us.us_recv? */ printf("\n"); printf("%d packets captured\n", pkt_captured); printf("%d packets received by filter\n", us.bs_recv); printf("%d packets dropped by kernel\n", us.bs_drop); /* * Destroy the usbusN interface only if it was created by * usbdump(8). Ignore when it was already destroyed. */ if (ifindex == 0 && if_nametoindex(i_arg) > 0) { (void)strlcpy(ifr.ifr_name, i_arg, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) warn("SIOCIFDESTROY ioctl failed"); } close(s); if (p->fd > 0) close(p->fd); if (p->rfd > 0) close(p->rfd); if (p->wfd > 0) close(p->wfd); if (p->bfd > 0) close(p->bfd); return (EXIT_SUCCESS); }