Hello,
Just a quick skim over it.
Damien Zammit, le lun. 07 juil. 2025 09:26:54 +0000, a ecrit:
> +int
> +main (int argc, char **argv)
> +{
> +
> + /* Make sure we will not swap out,
> + * because dma buffers for net drivers don't work otherwise */
> + err = wire_task_self ();
> + if (err)
> + error (1, err, "cannot lock all memory");
Will they not? vm_allocate_contiguous makes the allocation non-pageable,
so we shouldn't need to wire the whole rumpnet process (which would
again eat a lot of memory). If we use dma on memory that was not
allocated with vm_allocate_contiguous, rump should be able to wire just
the memory that is being used for dma during the transfer.
> +struct ifreq *
> +search_interface(const char *ifname)
> +{
> + struct ifreq ifr;
> + struct ifreq *dev = NULL;
> + char *last_slash, *name;
> +
> + memset(&ifr, 0, sizeof(ifr));
> + last_slash = strrchr(ifname, '/');
> + if (!last_slash)
> + name = ifname;
> + else
> + name = last_slash + 1;
> + dev = calloc(1, sizeof(*dev));
You can just malloc it, since you overwrite it later on.
> + if (dev == NULL)
> + {
> + mach_print("ERROR: cannot calloc\n");
> + return NULL;
> + }
> + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
> + if (rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, &ifr) == -1)
> + {
> + mach_print("siocgifflags failed: ");
> + mach_print(name);
> + mach_print("\n");
> + goto errexit;
> + }
> + *dev = ifr;
> + goto exit;
> +
> +errexit:
> + free(dev);
> + dev = NULL;
> +exit:
> + return dev;
> +}
> +
> +static io_return_t
> +init_interface(struct net_data *nd)
> +{
> + err = rump_sys_ioctl (nd->bpf_fd, BIOCSRTIMEOUT, &timeout);
> + if (err < 0)
> + {
> + mach_print("ERROR: biocsrtimeout failed\n");
> + errno = rump_errno2host(err);
> + return errno;
> + }
Do we really need this? Since we enable BIOCIMMEDIATE we should not need
a timeout, and that will only provide spurious wakeups that eat cpu for
no use.
> +static io_return_t
> +send_packet (struct net_data *nd, io_buf_ptr_t buf, unsigned int bytes)
> +{
> + io_return_t err;
> + struct ethhdr *hdr = (struct ethhdr *)buf;
> + struct iovec iov[2];
> + int result;
> + int flag;
> + int i;
> +
> + if ((ntohs(hdr->h_proto) != ETH_P_IP)
> + && (ntohs(hdr->h_proto) != ETH_P_ARP)
> + && (ntohs(hdr->h_proto) != ETH_P_IPV6))
> + {
> + char proto[16] = {0};
> + snprintf(proto, 16, "0x%04x\n", ntohs(hdr->h_proto));
> + mach_print("ERROR: ethernet frame has wrong protocol: ");
Do we really need to filter out unknown protocols? We'd then have to
change this when porting e.g. STP or whatnot. We can as well just accept
sending anything.
> + mach_print(proto);
> + errno = EPROTO;
> + return -1;
> + }
> +
> + iov[0].iov_base = hdr;
> + iov[0].iov_len = ETH_HLEN;
> + iov[1].iov_base = hdr + 1;
> + iov[1].iov_len = bytes - ETH_HLEN;
Does it not work by just passing hdr in one go? struct ethhdr should
already be ETH_HLEN-sized, and that'll save a copy further down in the
stack.
> + result = rump_sys_writev (nd->bpf_fd, iov, 2);
> + if (result < 0)
> + {
> + errno = EIO;
> + mach_print("ERROR: rump_sys_writev(bpf)\n");
> + return -1;
> + }
> +
> +// for (i = 0; i < bytes; i++)
> +// {
> +// char dmp[4] = {0};
> +// snprintf(dmp, 4, "%02x ", ((uint8_t *)hdr)[i]);
> +// mach_print(dmp);
> +// }
> + return result;
> +}
> +static io_return_t
> +rumpnet_device_write (void *d, mach_port_t reply_port,
> + mach_msg_type_name_t reply_port_type, dev_mode_t mode,
> + recnum_t bn, io_buf_ptr_t data, unsigned int count,
> + int *bytes_written)
> +{
> + struct net_data *nd = (struct net_data *)d;
> + error_t err;
> + char msg[16] = {0};
Unused?
> +
> + err = send_packet(nd, data, count);
> + if (err < 0)
> + return errno;
> + *bytes_written = err;
> +
> + if (*bytes_written != count)
> + {
> + mach_print("ERROR: bytes_written != count\n");
> + return D_IO_ERROR;
> + }
> +
> + vm_deallocate (mach_task_self (), (vm_address_t) data, count);
> +
> + return D_SUCCESS;
> +}
> +
> +static io_return_t
> +device_get_status (void *d, dev_flavor_t flavor, dev_status_t status,
> + mach_msg_type_number_t *count)
> +{
> + struct net_data *nd = (struct net_data *)d;
> + io_return_t err;
> +
> + if (flavor == NET_FLAGS)
> + {
Better use switch/case?
> + if (*count != 1)
> + return D_INVALID_SIZE;
> +
> + err = rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, nd->dev);
> + if (err < 0)
> + return D_IO_ERROR;
> +
> + *(int *) status = nd->dev->ifr_flags;
> + }
> + else if (flavor == NET_STATUS)
> + {
> + struct net_status *ns = (struct net_status *)status;
> +
> + if (*count < NET_STATUS_COUNT)
> + return D_INVALID_OPERATION;
> +
> + ns->min_packet_size = ETH_HLEN;
> + ns->max_packet_size = SIZEOF_ETH_FRAME;
> + ns->header_format = HDR_ETHERNET;
> + ns->header_size = ETH_HLEN;
> + ns->address_size = ETH_ALEN;
> + ns->flags = nd->dev->ifr_flags;
> + ns->mapped_size = 0;
> +
> + *count = NET_STATUS_COUNT;
> + }
> + else if (flavor == NET_ADDRESS)
> + {
> + err = rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, nd->dev);
> + if (err < 0)
> + return D_IO_ERROR;
> +
> + err = get_hwaddr(nd->dev->ifr_name, nd->hw_address);
> + if (err)
> + return D_IO_ERROR;
> +
> + status[0] =
> + ((nd->hw_address[0] << 24) |
> + (nd->hw_address[1] << 16) |
> + (nd->hw_address[2] << 8) |
> + (nd->hw_address[3]));
> +
> + status[1] =
> + ((nd->hw_address[4] << 24) |
> + (nd->hw_address[5] << 16));
> +
> + *count = 2;
> + }
> + return D_SUCCESS;
> +}
BTW, does tcpdump work on it? We will definitely want that working :)
Samuel