Skip to content

pnal_udp_open does not bind to the network device on linux #590

@arneboe

Description

@arneboe

I have a use case where I have two network interfaces in the same subnet.
I am running two instances of the p-net stack, one for each interface.

For this use case, it is important to bind the udp socket to the device using the device name.
If I don't do this bind, the p-net stack will randomly use the wrong interface to send udp messages.

To fix this, I have added a reverse lookup loop to pnal_udp_open in the linux port.
It searches for the network interface with the given ip and sets the SO_BINDTODEVICE socket option to bind to the device by name.
For this to work, pf_udp_open needs to be modified as well. Instead of handing PNAL_IPADDR_ANY it should use the ip from net->fspm_cfg.

As far as I know, binding to the device by name does not have any downsides and I would like you to include this feature in your next release.

Below is my proof of concept implementation:

int pf_udp_open (pnet_t * net, pnal_ipport_t port)
{
   const uint32_t ip_addr =
      (((uint32_t)net->fspm_cfg.if_cfg.ip_cfg.ip_addr.a) << 24) |
      (((uint32_t)net->fspm_cfg.if_cfg.ip_cfg.ip_addr.b) << 16) |
      (((uint32_t)net->fspm_cfg.if_cfg.ip_cfg.ip_addr.c) << 8) |
      ((uint32_t)net->fspm_cfg.if_cfg.ip_cfg.ip_addr.d);

   return pnal_udp_open (ip_addr, port);
}
int pnal_udp_open (pnal_ipaddr_t addr, pnal_ipport_t port)
{
   struct sockaddr_in local;
   int id;
   const int enable = 1;

   id = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
   if (id == -1)
   {
      return -1;
   }

   if (setsockopt(id, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) != 0)
   {
      goto error;
   }

   /* Find the network interface with the matching IP address */
   struct ifaddrs *ifaddr, *ifa;
   int found_interface = 0;

   if (getifaddrs (&ifaddr) == 0)
   {
      /* Walk through linked list, looking for matching IP */
      for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
      {
         if (ifa->ifa_addr == NULL)
            continue;

         if (ifa->ifa_addr->sa_family == AF_INET)
         {
            struct sockaddr_in * ifaddr_in =
               (struct sockaddr_in *)ifa->ifa_addr;
            if (ntohl (ifaddr_in->sin_addr.s_addr) == addr)
            {
               /* Found matching interface, bind socket to it */
               if (
                  setsockopt (
                     id,
                     SOL_SOCKET,
                     SO_BINDTODEVICE,
                     ifa->ifa_name,
                     strlen (ifa->ifa_name)) == 0)
               {
                  printf ("Socket bound to interface: %s\n", ifa->ifa_name);
                  found_interface = 1;
                  break;
               }
            }
         }
      }

      freeifaddrs (ifaddr);

      if (!found_interface)
      {
         printf (
            "Warning: Could not find interface with IP address 0x%08x\n",
            addr);
      }
   }

   /* set IP and port number */
   local = (struct sockaddr_in){
      .sin_family = AF_INET,
      .sin_addr.s_addr = htonl (addr),
      .sin_port = htons (port),
      .sin_zero = {0},
   };

   if (bind (id, (struct sockaddr *)&local, sizeof (local)) != 0)
   {
      goto error;
   }

   return id;

error:
   close (id);
   return -1;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions