The information that I collected about netlink multicast is all between kernal and userspace.
Although I know it maybe a better way to use Shared Memory for my situation, by I need to use Netlink for multicast IPC between userspace and userspace. I have already constructed a demo with a netlink receiver and sender, but I encountered a few issues due to my limited understanding of Netlink.
Here is my simple netlink demo for receiver:
#include <linux/netlink.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <cstring>
#include <string>
#define NLINK_MSG_LEN 1024
int main() {
// Question:
// when I use `NETLINK_USERSOCK` in the receiver program, the system informs
// me of insufficient permissions to bind fd, and I have to usesudo, but I
// prefer not to.
int fd = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
printf("receiver is ready!\n");
if (fd < 0) {
perror("create Netlink socket failed!\n");
exit(1);
}
struct sockaddr_nl listen_addr;
listen_addr.nl_family = AF_NETLINK; // AF_NETLINK socket protocol
listen_addr.nl_pid = getpid(); // application unique id
listen_addr.nl_groups =
(1 << 3); // specify the address which the process want to receive
// if two or more address then turn on those bits
// subscribe to 3 multicast address
if (::bind(fd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) != 0) {
perror("bind netlink socket fail");
exit(1);
}
printf("Listening to the MCAST address (3): %d\n", listen_addr.nl_groups);
printf("receiver pid [%u]\n", getpid());
struct nlmsghdr* nlh = (struct nlmsghdr*)malloc(NLMSG_SPACE(NLINK_MSG_LEN));
nlh->nlmsg_len = NLMSG_SPACE(NLINK_MSG_LEN);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
struct iovec iov;
iov.iov_base = reinterpret_cast<void*>(nlh);
iov.iov_len = nlh->nlmsg_len;
// we can get sender address from sender_addr
struct sockaddr_nl sender_addr;
struct msghdr msg;
msg.msg_name = reinterpret_cast<void*>(&sender_addr);
msg.msg_namelen = sizeof(sender_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// try to receive netlink message
while (1) {
ssize_t bytes_received = ::recvmsg(fd, &msg, 0);
if (bytes_received == -1) {
perror("receive message fail\n");
} else if (bytes_received == 0) {
printf("connection closed by remote peer\n");
} else {
printf("receive message from [%u]\n", sender_addr.nl_pid);
printf("receive message: %s\n", reinterpret_cast<char*>(NLMSG_DATA(nlh)));
}
}
close(fd);
}
my demo for sender:
#include <linux/netlink.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <cstring>
#include <string>
constexpr uint32_t NLINK_MSG_LEN = 1024;
int main() {
// **Question**:when I use `NETLINK_GENERIC` in the sender program, the system
// informs me of insufficient permissions to send message, and I have to use
// sudo, but I prefer not to.
int fd = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
printf("sender is ready!\n");
if (fd < 0) {
perror("create Netlink socket failed!");
exit(1);
}
struct sockaddr_nl dest_addr;
dest_addr.nl_family = AF_NETLINK;
// **Question**: Online recommendations suggest using the process ID as
// nl_pid, but I cannot obtain the process ID of the receiver. Is there a way
// to configure only nl_groups, so that all receivers in this nl_groups can
// receive messages?
//
// Moreover, what's strange is that even if I arbitrarily set a number for
// nl_pid here, although it throws a 'Connection refused' error, all the
// receivers in nl_groups can still receive messages.
dest_addr.nl_pid = 100;
dest_addr.nl_groups = (1 << 3);
// constructor netlink message
struct nlmsghdr* nlh = (struct nlmsghdr*)malloc(NLMSG_SPACE(NLINK_MSG_LEN));
::memset(nlh, 0, NLMSG_SPACE(NLINK_MSG_LEN));
nlh->nlmsg_len = NLMSG_SPACE(NLINK_MSG_LEN);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
struct iovec iov;
iov.iov_base = reinterpret_cast<void*>(nlh);
iov.iov_len = nlh->nlmsg_len;
struct msghdr msg;
msg.msg_name = reinterpret_cast<void*>(&dest_addr);
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// send message
::memcpy(reinterpret_cast<char*>(NLMSG_DATA(nlh)), "Hello World !", 14);
ssize_t bytes_sent = ::sendmsg(fd, &msg, 0);
printf("sender pid [%u]\n", getpid());
if (bytes_sent == -1) {
perror("send message fail\n");
} else {
printf("send message [%s]\n", reinterpret_cast<char*>(NLMSG_DATA(nlh)));
}
::close(fd); // close the socket
}
Here are my serveral questions, and some of them have already been raised in the demo code.
Question 1
I don't want to require sudo permission to execute the code. However, when I use NETLINK_USERSOCK in the Receiver or NETLINK_GENERIC in the Sender, it prompts me for insufficient permissions.
Question 2
I wish to send messages to all receivers corresponding to nl_groups in the sender code without specifying dest_addr.nl_pid.
Moreover, it is strange that the sender program can send to the nl_groups's all receiver but it would raise the Connection refused problem.
Question 3
If any question abound my code, please let me know, thanks~
