Get destination address of a received UDP packet
Asked Answered
M

2

26

Upon receiving a UDP packet, I need to respond to the sender with the address he used to send the packet to which I'm replying.

The recvfrom call lets me get the address of the sender, but how do I get the destination address of the received packet, which should match the address of one of the local host's interfaces?

Mysticism answered 12/3, 2011 at 8:7 Comment(11)
I'm curious why getsockname(2) didn't work. :)Viridissa
@sarnold: getsockname on a listening socket bound to 0.0.0.0:0 (or [::]:0) isn't that useful. With TCP you have a local address after accept, but with UDP… I'm not sure how to answer OP's question.Conation
@ephemient, when Matt first asked this question, I suggested connecting his UDP sockets so he could use getsockname(2). It sounded like it was going to work :) and now I've got a personal stake in finding him a solution. :)Viridissa
Similar question: #3941112Ilbert
@sarnold, this actually does work, though I don't know if this toy example answers the OP's question. (if multiple source addresses could be chosen for a different destination, or the reply address might be different than the default send address, it wouldn't work)Noelnoelani
By the way, an alternate method is to create a separate UDP socket per interface, bound to each interface. Then the socket you receive data on is directly associated with the interface the data was received by.Mariel
possible duplicate of How to tell which interface the socket received the message from?Mariel
See also Setting the source IP for a UDP socketScouting
@JasonC This is not a duplicate. Destination address and destination interface are two distinct things. An interface can have and often has multiple addresses. And one address can be assigned to two interfaces at least on Linux, even though I don't know about a good use case for that.Galengalena
@Pavel It is a duplicate. Destination address vs interface are both the same fundamental question of how to retrieve destination information for a UDP packet. And the answer is identical (in fact, the OP himself has the same good answer posted on both questions, here and there). Use IP_PKTINFO. Pick your most relevant field in the structure.Mariel
@JasonC As you wish. I stated my position, you stated yours.Galengalena
S
18

You set the IP_PKTINFO option using setsockopt and then use recvmsg and get a in_pktinfo structure in the msg_control member of struct msghdr. the in_pktinfo has a field with the destination address of the packet.

See: http://www.linuxquestions.org/questions/programming-9/how-to-get-destination-address-of-udp-packet-600103/ where I found the answer for more details.

Sisson answered 12/3, 2011 at 9:29 Comment(1)
I've provided a full working example in this answer.Mysticism
M
27

I've constructed an example that extracts the source, destination and interface addresses. For brevity, no error checking is provided.

// 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
}
Mysticism answered 15/3, 2011 at 8:30 Comment(11)
I am curious whether there is a way to get the destination address without the for-loop...looking for O(1) operation.Barkeeper
Curious to name the destination address as 'peer address'. The peer address is really the source address.Amphora
@EJP: It's only the source when receiving packets.Mysticism
@Hei: No. In order to provide O(1) lookup, you'd need to pack the ancillary control structures into a data container providing O(1) lookup. Populating that structure is at least O(n) the number of ancillary control messages. You should not worry about the complexity of the lookup for the control message you want: There are always only a few, and only those ones you've requested. There is also zero memory management required on your behalf. It's negligible overhead.Mysticism
Receiving packets is the topic of this question.Amphora
It's O(n) where n is the number of control messages. Since there's roughly same number of control messages per datagram, and the number does not depend on the number of packets received or on any other property of the packet, it's still constant time relative to what you care about -- just an average constant amount of added overhead (the same way we say hash table lookups are amortized constant time, even though they're linear relative to items per bucket).Mariel
Cool, this works! But I see the original destination address (for example multicast address) in 'ipi_addr' and I see the receiving interface address in 'ipi_spec_dst' which is also what I see in the 'ip(7)' manpage.Banana
@JohannesOvermann: I might have ipi_spec_dst and ipi_addr back to front in my code?Mysticism
@Matt: I think, just your comments for ipi_spec_dst and ipi_addr are the wrong way round. See man 7 ip on Linux: struct in_addr ipi_spec_dst; /* Local address / struct in_addr ipi_addr; / Header Destination address */. But yes, I would find your interpretation of the name more intuitive, too! :-) But these are details. Thanks for the good solution and for the code!Banana
How can I receive the packet itself after this?Friulian
aww, too bad, that’s Linux-specific and not available on at least some BSDsCleopatra
S
18

You set the IP_PKTINFO option using setsockopt and then use recvmsg and get a in_pktinfo structure in the msg_control member of struct msghdr. the in_pktinfo has a field with the destination address of the packet.

See: http://www.linuxquestions.org/questions/programming-9/how-to-get-destination-address-of-udp-packet-600103/ where I found the answer for more details.

Sisson answered 12/3, 2011 at 9:29 Comment(1)
I've provided a full working example in this answer.Mysticism

© 2022 - 2024 — McMap. All rights reserved.