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

Reply via email to