如何使用netlink进行IPC?

Posted

技术标签:

【中文标题】如何使用netlink进行IPC?【英文标题】:How to use netlink for IPC? 【发布时间】:2019-10-08 06:50:46 【问题描述】:

我的项目需要使用Netlink做IPC,参考是Netlink unicast。我对代码做了一些更改。首先,我启动了接收者和发送者,然后将发送者的 pid 输入到接收者,然后将接收者的 pid 输入到发送者。但是接收者没有收到消息。我也参考了Is anyone using netlink for IPC?,他们都没有工作。我的代码有什么问题吗?

收货人代码:

#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdio.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>

#define NLINK_MSG_LEN 1024
#define NETLINK_USER 31

int main() 
  int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  //int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
  //int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  printf("Inside recv main\n");

  if (fd < 0) 
    printf("Socket creation failed. try again\n");
    return -1;
  

  /* Declare for src NL sockaddr, dest NL sockaddr, nlmsghdr, iov, msghr */
  struct sockaddr_nl src_addr, dest_addr;
  memset(&src_addr, 0, sizeof(src_addr));
  memset(&dest_addr, 0, sizeof(dest_addr));

  //allocate buffer for netlink message which is message header + message payload
  struct nlmsghdr *nlh =(struct nlmsghdr *) malloc(NLMSG_SPACE(NLINK_MSG_LEN));
  memset(nlh, 0, NLMSG_SPACE(NLINK_MSG_LEN));

  //fill the iovec structure
  struct iovec iov;
  memset(&iov, 0, sizeof(iov));
  //define the message header for message
  //sending
  struct msghdr msg;
  memset(&msg, 0, sizeof(msg));

  int sender_pid;
  printf("Receiver process id: %d\n", getpid());
  printf("Sender process id: ");
  scanf("%d", &sender_pid);

  src_addr.nl_family = AF_NETLINK;   //AF_NETLINK socket protocol
  src_addr.nl_pid = getpid();               //application unique id
  src_addr.nl_groups = 0;            //specify not a multicast communication

  dest_addr.nl_family = AF_NETLINK; // protocol family
  dest_addr.nl_pid = sender_pid;   //destination process id
  dest_addr.nl_groups = 0; 

  nlh->nlmsg_len = NLMSG_SPACE(NLINK_MSG_LEN);   //netlink message length 
  nlh->nlmsg_pid = getpid();                            //src application unique id
  nlh->nlmsg_flags = 0;

  iov.iov_base = (void *)nlh;     //netlink message header base address
  iov.iov_len = nlh->nlmsg_len;   //netlink message length
  msg.msg_name = (void *)&dest_addr;
  msg.msg_namelen = sizeof(dest_addr);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  //attach socket to unique id or address
  if(bind(fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) 
    printf("Bind failed");
    return -1;
  

  /* Listen forever in a while loop */
  while (1) 
    //receive the message
    recvmsg(fd, &msg, 0);
    printf("Received message: %s\n", (char *)NLMSG_DATA(nlh));
  
  close(fd); // close the socket

发件人代码:

#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdio.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>

#define NLINK_MSG_LEN 1024
#define NETLINK_USER 31

int main() 
  int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  //int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
  //int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  printf("Inside send main\n");

  if (fd < 0) 
    printf("Socket creation failed. try again\n");
    return -1;
  

  /* Declare for src NL sockaddr, dest NL sockaddr, nlmsghdr, iov, msghr */
  struct sockaddr_nl src_addr, dest_addr;
  memset(&src_addr, 0, sizeof(src_addr));
  memset(&dest_addr, 0, sizeof(dest_addr));

  //allocate buffer for netlink message which is message header + message payload
  struct nlmsghdr *nlh =(struct nlmsghdr *) malloc(NLMSG_SPACE(NLINK_MSG_LEN));
  memset(nlh, 0, NLMSG_SPACE(NLINK_MSG_LEN));

  //fill the iovec structure
  struct iovec iov;
  memset(&iov, 0, sizeof(iov));
  //define the message header for message
  //sending
  struct msghdr msg;
  memset(&msg, 0, sizeof(msg));

  int receiver_pid;
  printf("Sender process id: %d\n", getpid());
  printf("Receiver process id: ");
  scanf("%d", &receiver_pid);

  src_addr.nl_family = AF_NETLINK;   //AF_NETLINK socket protocol
  src_addr.nl_pid = getpid();               //application unique id
  src_addr.nl_groups = 0;            //specify not a multicast communication

  dest_addr.nl_family = AF_NETLINK; // protocol family
  dest_addr.nl_pid = receiver_pid;   //destination process id
  dest_addr.nl_groups = 0; 

  nlh->nlmsg_len = NLMSG_SPACE(NLINK_MSG_LEN);   //netlink message length 
  nlh->nlmsg_pid = getpid();                            //src application unique id
  nlh->nlmsg_flags = 0;
  strcpy(NLMSG_DATA(nlh), "Hello World !");   //copy the payload to be sent

  iov.iov_base = (void *)nlh;     //netlink message header base address
  iov.iov_len = nlh->nlmsg_len;   //netlink message length
  msg.msg_name = (void *)&dest_addr;
  msg.msg_namelen = sizeof(dest_addr);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  //attach socket to unique id or address
  if(bind(fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) 
    printf("Bind failed");
    return -1;
  

  //send the message
  sendmsg(fd, &msg, 0);
  printf("Send message %s\n", (char *)NLMSG_DATA(nlh));

  close(fd); // close the socket

【问题讨论】:

【参考方案1】:

您对socket 使用了错误的协议。唯一可以做你想做的事,用户空间进程之间的IPC,是NETLINK_USERSOCK

请记住,在您安排与 PID 通信的方式中,您可能会错过一些消息...为了验证通信是否发生,您可以将 msg_nameNULL 留给接收者 并且不会产生不良影响 - 这样做并更改协议,您的代码将按预期工作。

【讨论】:

我将fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC); 更改为fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK) 并将msg.msg_name = (void *)&amp;dest_addr 更改为msg.msg_name = NULL,但仍然无法正常工作。 我将接收者和发送者都更改为msg.msg_name = NULL,它不起作用。但我意识到并将发件人只改回msg.msg_name = (void *)&amp;dest_addr,它成功了,谢谢! 抱歉,我意识到我在这一点上太含糊了 - 修正了文本。很高兴你知道了!

以上是关于如何使用netlink进行IPC?的主要内容,如果未能解决你的问题,请参考以下文章

linux下netlink的使用简介

如何使用 netlink 库向网络接口发送命令

C 语言网络编程 — PF_NETLINK sockets

C 语言网络编程 — PF_NETLINK sockets

Linux内核通信之netlink机制

如何使用管道进行非阻塞 IPC(UART 仿真)