LaForge's home page (Posts about network)https://laforge.gnumonks.org/blog/tags/network.atom2022-06-21T07:49:56ZHarald WelteNikolaObtaining the local IP address of an unbound UDP sockethttps://laforge.gnumonks.org/blog/20171020-local_ip_unbound_udp/2017-10-20T00:00:00+02:002017-10-20T00:00:00+02:00Harald Welte<p>Sometimes one is finding an interesting problem and is surprised that
there is not a multitude of blog post, stackoverflow answers or the like
about it.</p>
<p>A (I think) not so uncommon problem when working with datagram sockets
is that you may want to know the local IP address that the OS/kernel
chooses when sending a packet to a given destination.</p>
<p>In an unbound UDP socket, you basically send and receive packets with
any number of peers from a single socket. When sending a packet to
destination Y, you simply pass the destination address/port into the
<code class="docutils literal">sendto()</code> socket function, and the OS/kernel will figure out which of
its local IP addresses will be used for reaching this particular
destination.</p>
<p>If you're a dumb host with a single default router, then the answer to
that question is simple. But in any reasonably non-trivial use case,
your host will have a variety of physical and/or virtual network devices
with any number of addresses on them.</p>
<p>Why would you want to know that address? Because maybe you need to
encode that address as part of a packet payload. In the current use
case that we have, it is the <a class="reference external" href="https://git.osmocom.org/osmo-mgw">OsmoMGW</a>, implementing the IETF
<a class="reference external" href="https://tools.ietf.org/html/rfc3435">MGCP Media Gateway Control Protocol</a>.</p>
<p>So what can you do? You can actually create a new "trial" socket, not
bind it to any specific local address/port, but <code class="docutils literal">connect()</code> it to the
destination of your IP packets. Then you do a <code class="docutils literal">getsockname()</code>, which
will give you the local address/port the kernel has selected for this
socket. And that's exactly the answer to your question. You can now
close the "trial" socket and have learned which local IP address the
kernel would use if you were to send a packet to that destination.</p>
<p>At least on Linux, this works. While <code class="docutils literal">getsockname()</code> is standard BSD
sockets API, I'm not sure how portable it is to use it on a socket that
has not been explicitly bound by a prior call to <code class="docutils literal">bind()</code>.</p>