| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <zircon/device/ethernet.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| #include <pretty/hexdump.h> |
| |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #define BUFSIZE 2048 |
| |
| void handle_rx(zx_handle_t rx_fifo, char* iobuf, unsigned count) { |
| eth_fifo_entry_t entries[count]; |
| |
| for (;;) { |
| uint32_t n; |
| zx_status_t status; |
| if ((status = zx_fifo_read(rx_fifo, entries, sizeof(entries), &n)) < 0) { |
| if (status == ZX_ERR_SHOULD_WAIT) { |
| zx_object_wait_one(rx_fifo, ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED, ZX_TIME_INFINITE, NULL); |
| continue; |
| } |
| fprintf(stderr, "netdump: failed to read rx packets: %d\n", status); |
| return; |
| } |
| |
| eth_fifo_entry_t* e = entries; |
| for (uint32_t i = 0; i < n; i++, e++) { |
| if (e->flags & ETH_FIFO_RX_OK) { |
| printf("---\n"); |
| hexdump8_ex(iobuf + e->offset, e->length, 0); |
| } |
| e->length = BUFSIZE; |
| e->flags = 0; |
| uint32_t actual; |
| if ((status = zx_fifo_write(rx_fifo, e, sizeof(*e), &actual)) < 0) { |
| fprintf(stderr, "netdump: failed to queue rx packet: %d\n", status); |
| break; |
| } |
| } |
| } |
| } |
| |
| int main(int argc, char** argv) { |
| if (argc != 2) { |
| fprintf(stderr, "usage: netdump <network-device>\n"); |
| return -1; |
| } |
| |
| int fd; |
| if ((fd = open(argv[1], O_RDWR)) < 0) { |
| fprintf(stderr, "netdump: cannot open '%s'\n", argv[1]); |
| return -1; |
| } |
| |
| eth_fifos_t fifos; |
| zx_status_t status; |
| |
| ssize_t r; |
| if ((r = ioctl_ethernet_get_fifos(fd, &fifos)) < 0) { |
| fprintf(stderr, "netdump: failed to get fifos: %zd\n", r); |
| return r; |
| } |
| |
| unsigned count = fifos.rx_depth / 2; |
| zx_handle_t iovmo; |
| // allocate shareable ethernet buffer data heap |
| if ((status = zx_vmo_create(count * BUFSIZE, 0, &iovmo)) < 0) { |
| return -1; |
| } |
| |
| char* iobuf; |
| if ((status = zx_vmar_map(zx_vmar_root_self(), 0, iovmo, 0, count * BUFSIZE, |
| ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, |
| (uintptr_t*)&iobuf)) < 0) { |
| return -1; |
| } |
| |
| if ((r = ioctl_ethernet_set_iobuf(fd, &iovmo)) < 0) { |
| fprintf(stderr, "netdump: failed to set iobuf: %zd\n", r); |
| return -1; |
| } |
| |
| if ((r = ioctl_ethernet_set_client_name(fd, "netdump", 7)) < 0) { |
| fprintf(stderr, "netdump: failed to set client name %zd\n", r); |
| } |
| |
| // assign data chunks to ethbufs |
| for (unsigned n = 0; n < count; n++) { |
| eth_fifo_entry_t entry = { |
| .offset = n * BUFSIZE, |
| .length = BUFSIZE, |
| .flags = 0, |
| .cookie = NULL, |
| }; |
| uint32_t actual; |
| if ((status = zx_fifo_write(fifos.rx_fifo, &entry, sizeof(entry), &actual)) < 0) { |
| fprintf(stderr, "netdump: failed to queue rx packet: %d\n", status); |
| return -1; |
| } |
| } |
| |
| if (ioctl_ethernet_start(fd) < 0) { |
| fprintf(stderr, "netdump: failed to start network interface\n"); |
| return -1; |
| } |
| |
| if (ioctl_ethernet_tx_listen_start(fd) < 0) { |
| fprintf(stderr, "netdump: failed to start listening\n"); |
| return -1; |
| } |
| |
| handle_rx(fifos.rx_fifo, iobuf, count); |
| |
| return 0; |
| } |