Question

If a socket is bound to IN6ADDR_ANY or INADDR_ANY and you use a call such as recvfrom() to receive messages on the socket. Is there a way to find out which interface the message came from?

In the case of IPv6 link-scope messages, I was hoping that the from argument of recvfrom() would have the scope_id field initialized to the interface Id. Unfortunately it is set to 0 in my test program.

Anybody know of a way to find out this information?

Was it helpful?

Solution

Apart from binding to each interface, I'm not aware of a way with IPv4, per se.

IPv6 has added the IPV6_PKTINFO socket option to address this shortcoming. With that option in effect, a struct in6_pktinfo will be returned as ancillary data.

OTHER TIPS

dwc is right, IPV6_PKTINFO will work for IPv6 on Linux.

Moreover, IP_PKTINFO will work for IPv4 — you can see details in manpage ip(7)

I've constructed an example that extracts the source, destination and interface addresses. For brevity, no error checking is provided. See this duplicate: Get destination address of a received UDP packet.

// sock is bound AF_INET socket, usually SOCK_DGRAM
// include struct in_pktinfo in the message "ancilliary" control data
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
// the control data is dumped here
char cmbuf[0x100];
// the remote/source sockaddr is put here
struct sockaddr_in peeraddr;
// if you want access to the data you need to init the msg_iovec fields
struct msghdr mh = {
    .msg_name = &peeraddr,
    .msg_namelen = sizeof(peeraddr),
    .msg_control = cmbuf,
    .msg_controllen = sizeof(cmbuf),
};
recvmsg(sock, &mh, 0);
for ( // iterate through all the control headers
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR(&mh, cmsg))
{
    // ignore the control headers that don't match what we want
    if (cmsg->cmsg_level != IPPROTO_IP ||
        cmsg->cmsg_type != IP_PKTINFO)
    {
        continue;
    }
    struct in_pktinfo *pi = CMSG_DATA(cmsg);
    // at this point, peeraddr is the source sockaddr
    // pi->ipi_spec_dst is the destination in_addr
    // pi->ipi_addr is the receiving interface in_addr
}

Its been a while since I've been doing C/C++ TCP/IP coding but as far as I remember on every message (or derived socket) you can get into the IP headers information. These headers should include the receiving address which will be the IP of the interface you are asking about.

Outside of opening a separate socket on each interface as Glomek suggested, the only way I know to do this definitively on Windows is to use a raw socket, e.g.,

  SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

Each receive from this socket will be an IP packet, which contains both the source and destination addresses. The program I work on requires me to put the socket in promiscuous mode using the SIO_RCVALL option. Doing this means I get every IP packet the interface "sees" on the network. To extract packets expressly for my application requires me to filter the data using the addresses and ports in the IP and TCP/UDP headers. Obviously, that's probably more overhead than you're interested in. I only mention it to say this - I've never used a raw socket without putting it in promiscuous mode. So I'm not sure if you can bind it to INADDR_ANY and just use it as a regular socket from that point forward or not. It would seem to me that you can; I've just never tried it.

EDIT: Read this article for limitations regarding raw sockets on Windows. This biggest hurdle I faced on my project was that one has to be a member of the Administrators group to open a raw socket on Windows 2000 and later.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top