0

How do I find out which IP address the OS chose to use, when I send a packet from a UDP socket bound to INADDR_ANY?

int s = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in src;
src.sin_family = AF_INET;
src.sin_port = 12345;
src.sin_addr.s_addr = INADDR_ANY;
bind(s, (struct sockaddr*)&src, sizeof(src));

char msg[] = "Hello";
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = 12345;
dest.sin_addr.s_addr = (in_addr_t)0xdeadbeef;
sendto(s, msg, sizeof(msg), 0, (struct sockaddr*)&dest, sizeof(dest));

What source address was used to send the packet? Ideally there would be a sendtofrom() function like recvfrom() which returns the address the kernel chose.

In my application, I bind to INADDR_ANY and send a packet to a STUN server (I don't want to have to play around with the routing table to pick a source address: the kernel's choice is fine). But, to do ICE I then need to obtain the "base address" of the "server-reflexive address", ie. I need to know which local address was used to send the STUN request. I would accept Windows- or POSIX-specific answers suggesting looking up an address from the routing table.

Nicholas Wilson
  • 9,435
  • 1
  • 41
  • 80

2 Answers2

1

Unfortunately, there is no way to query which source IP address is used. On Windows, for example, you would have to write a low-level NDIS driver to get that info. However, if you use WSASendMsg()/sendmsg() instead of sendto(), you can pass in an in_pktinfo structure to specify the source IP address you want to use for the outgoing packet.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • That's very helpful. I've also been reading up on `IP_PKTINFO`/`IP_RECVDSTADDR` (http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket) which let you find out the destination address of incoming packets. It's a shame there's just the one bit of information I can't get in an easy way. I think I'll just send the packets using the interface used for the host's default route. – Nicholas Wilson Jan 07 '15 at 07:50
0

I encountered the same problem recently.

What I do to solve this problem is

  1. get the interface name from received packet
  2. bind socket to specific interface
  3. unbind socket

Example:

  struct ifreq ifr;
  ...
  recvmsg(fd, &msg...)
  ...
  //
  if (msg.msg_controllen >= sizeof(struct cmsghdr))
    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
      if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
      {
        iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
      }
  if_indextoname(iface_index , ifr.ifr_name);
  mret=setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));

  sendmsg(...);

  memset(&ifr, 0, sizeof(ifr));
  snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "");
  mret=setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
Chih-Ying Lin
  • 13
  • 1
  • 3