cnetlinkposix-selectipsec

recvmsg with select on NETLINK socket


I am writing NETLINK xfrm socket programming to create association and policies for ESP communication. To send the data to kernel

sockfd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_XFRM);
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sckbuff, sizeof(sckbuff));
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sckbuff, sizeof(sckbuff));
bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));
...
memset(&msg, 0, sizeof(struct msghdr));
msg.msg_name = (void *)&(addr);
msg.msg_namelen = sizeof(addr);
...
memset(&iov, 0, sizeof(struct iovec));
iov.iov_base = (void *)netlink_req;
iov.iov_len = netlink_req->nlmsg_len;

msg.msg_iov = &iov;
msg.msg_iovlen = 1;

sendmsg(sockfd, &msg, 0);

And to receive the response from kernel

while(true)
{
  ...
  FD_ZERO(&readfds);
  FD_SET(sockfd, &readfds);
  select(sockfd+1, &readfds, NULL, NULL, NULL);

  read_len = recvmsg(sockfd, &msg, MSG_PEEK);
  printf("Data received %d\n", read_len);
  for (nh = (struct nlmsghdr *) buf;
     NLMSG_OK (nh, read_len);
     nh = NLMSG_NEXT (nh, read_len))
  {
    if (nh->nlmsg_type == NLMSG_DONE)
    {
          break;
    }
    else if (nh->nlmsg_type == NLMSG_ERROR)
    {
      ...
    }
    ...
  }
}
...

It is working file if recvmsg is done sequentially without select.

But when i tried to send multiple request and do select with sockfd in another thread then recvmsg continue to receive the same message and never stop.

Is there any flag i need to set in response to stop the continuous loop or any other way?


Solution

  • recvmsg(), when called with the MSG_PEEK flag, returns data but doesn't remove it from the socket's receive queue, leading to the next receive returning the same data again.

    Remove the flag (or perform a second call for the same size without it) to ensure you get the following messages.

    I'd also highly recommend checking the return from select() as well as the updated set (since select() can return without a readable socket in this case, e.g. if a signal is caught). I'd also note that in your current usage, select() isn't giving you much over a plain blocking recvmsg() call.