0

I have a kernel module that communicates with userspace using Netlink, it looks like that

Kernel Module

#define NETLINK_USER 31
struct sock *nl_sk = NULL;

int init_module()
{
  struct netlink_kernel_cfg cfg = {
    .input = nl_recv_msg,
  };
    
  nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
  if (!nl_sk) return -1;
  return 0;
}
void cleanup_module()
{
  netlink_kernel_release(nl_sk);
}

And this is a C++ userspace app that communicates with it

Userspace C++

#define NETLINK_USER 31
#define APP_PORT 5007
#define TIMEOUT_SEC 3
int sock_fd = 0;

void Listen()
{
  nlmsghdr *nlh = NULL;
  iovec iov;
  msghdr msg;
  sockaddr_nl dest_addr, src_addr;

  sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  InitSocketParams(src_addr, dest_addr, nlh, iov, msg, sizeof(LD_EVENT));

  timeval timeout;
  timeout.tv_sec = TIMEOUT_SEC;
  timeout.tv_usec = 0;
  setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));

  while (isrunning)
  {
    int num_recv = recvmsg(sock_fd, &msg, 0);
    if (num_recv <= 0)
    {
      if (errno == EAGAIN) // EAGAIN is a periodic Timeout
        continue;
    }

    void *recv_st = NLMSG_DATA(nlh);
    ProcessMsg(recv_st);
  }

  free(nlh);
  close(sock_fd);
}

int Send(void *msg)
{
  sockaddr_nl src_addr, dest_addr;
  nlmsghdr *nlh = NULL;
  iovec iov;
  msghdr _msghdr;
  int ret = 0;

  sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  InitSocketParams(src_addr, dest_addr, nlh, iov, _msghdr, sizeof(*msg));

  memcpy(NLMSG_DATA(nlh), msg, sizeof(*msg));
  int bytes_sent = sendmsg(sock_fd, &_msghdr, 0);
  if (bytes_sent < 0)
    ret = errno;

  free(nlh);
  return ret;
}

void InitSocketParams(sockaddr_nl &src_addr, sockaddr_nl &dest_addr,
                      nlmsghdr *&nlh, iovec &iov, msghdr &msg, const int max_payload_size)
{
  memset(&msg, 0, sizeof(msg));
  memset(&src_addr, 0, sizeof(src_addr));
  memset(&dest_addr, 0, sizeof(dest_addr));

  src_addr.nl_family = AF_NETLINK;
  src_addr.nl_pid = APP_PORT; /* self pid */
  bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));

  dest_addr.nl_family = AF_NETLINK;
  dest_addr.nl_pid = 0;    /* For Linux Kernel */
  dest_addr.nl_groups = 0; /* unicast */

  if (nlh) free(nlh);
  nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(max_payload_size));
  memset(nlh, 0, NLMSG_SPACE(max_payload_size));
  nlh->nlmsg_len = NLMSG_SPACE(max_payload_size);
  nlh->nlmsg_pid = DAEMON_PORT;
  nlh->nlmsg_flags = 0;

  iov.iov_base = (void *)nlh;
  iov.iov_len = nlh->nlmsg_len;
  msg.msg_name = (void *)&dest_addr;
  msg.msg_namelen = sizeof(dest_addr);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
}

Close()
{
  if (sock_fd > 0)
    close(sock_fd);
}

int StartListener()
{
  isrunning = true;
  std::thread th(&Listen, this);
  m_watcherthread = std::move(th);
}

The problem is that when the userspace app is connected to the kernel module I can not rmmod the kernel module at all. It shows rmmod: ERROR: Module my_module is in use. I have to close the userspace app first to be able to rmmod the kernel module. Is there a way to do it without closing the userspace app?

Khaled Hamed
  • 85
  • 1
  • 3
  • 10
  • 1
    Why do you want to do this? Not a kernel expert here but this sounds like an incredibly weird thing to do - you almost certainly don't want the module to disappear while your app has an open file handle associated with it. It's probably best you either get your app to close the connection so you can reload the module and re-open it, or just live with the overhead of reloading your app. – John Graham Feb 04 '21 at 18:38
  • @JohnGraham I need both the kernel module and the userspace app to be able to disconnect/close at any time. I can handle the needed logic for the other part when it detects the disconnection. – Khaled Hamed Feb 08 '21 at 18:25
  • Sounds like you can just close the file handles from your userspace app - then you can remove the module if you like – John Graham Feb 09 '21 at 19:44

0 Answers0