Search code examples
clinuxsocketsipcnetlink

How to use netlink for IPC?


My project needs to do IPC by using Netlink and the reference is Netlink unicast. I had done some changes in code. Firstly, I started both receiver and sender, then I inputted the sender's pid to receiver and later inputted receiver's pid to sender. But the receiver didn't receive the message. I also refer Is anyone using netlink for IPC?, none of them were working. Is that any problem in my code?

Receiver's code:

#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
}

Sender's code:

#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
}

Solution

  • You are using the wrong kind of protocol to socket. The only one which can do what you want, IPC between user-space processes, is NETLINK_USERSOCK.

    Bear in mind that in the way you are arranging communication with PIDs, you might miss some messages... For the purposes of verifying the communication does happen, you can leave msg_name NULL for the receiver and that will have no ill effect(s) - doing so & changing protocol, your code works as expected.