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?