4

When binding a UDP socket to ("", 1234) or ("0.0.0.0", 1234), is it possible to find out what IP-address it will actually send from?

As you can see in the code below, getsockname only tells me what I bound to. But when I send a packet, I can see that the IP-address is, in my case, 10.0.0.2.

Do I have to infer this address myself by looking at my network interfaces? If so, that is fine, but is there a robust way of doing so?

from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("", 1234))
print(s.getsockname()) # prints ("0.0.0.0", 1234)
s.sendto("hello", ("10.0.0.3", 1234)) # sends from 10.0.0.2

I've tried doing

import socket
print(socket.gethostbyname(socket.gethostname()))

but that doesn't seem to be very reliable (in the case where I expected 10.0.0.2, I got 127.0.1.1).

I realize that by binding to 0.0.0.0, I bind to all local network interfaces. Does that mean that my source IP-address will be determined by the routing tables when I try to send something? If so, can I still get that address in a robust way from Python?

csl
  • 10,937
  • 5
  • 57
  • 89
  • 4
    By binding to all interfaces you can receive from all of them. When you send, the packet will get its source set from the actual interface the packet goes out on. You probably have to manually check the routing-table to know what interface will be used for a specific address. – Some programmer dude Apr 04 '14 at 08:30

2 Answers2

2

The IP address used when sending will be determined by the routing table as the packet is sent. There might be platform specific ways of querying that routing table, but a fairly portable way is to connect() the socket first.

You can use another socket just for querying this information too. e.g.

from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("", 1234))
print(s.getsockname()) # prints ("0.0.0.0", 1234)
sq = socket(AF_INET, SOCK_DGRAM)
sq.connect(("10.0.0.3", 1234))
print(sq.getsockname()[0])
sq.close()
s.sendto("hello", ("10.0.0.3", 1234)) # sends from 10.0.0.2
nos
  • 223,662
  • 58
  • 417
  • 506
  • Ingenious, and it works! Just one question, is there a purpose to the last `sendto`? Seems connecting and calling `getsockname` is all that I need. Thanks! – csl Apr 04 '14 at 15:15
  • 1
    @csl This was just your example, with your sendto(), but using a 2. socket for querying the information. If you only send to 1 destination, you can just use 1 socket and connect that socket. However, once connected, you can't send/receive packet for any other host/port combination on that socket. – nos Apr 04 '14 at 15:19
1

This is more a usually-you-do-not-need-it answer. It may not correspond to your usecase.

Have alook at socket.gethostbyname_ex(socket.gethostname()). It shows all possible IP-addresses and the host name. You can receive from all of those since you did not bind to any specific one of those. They will be your source ip addresses.

It is not necessairy for you to know the exact address you send from. The receiver may see another one if it goes behind a NAT, into the internet or through a VPN. The receiver will then know where the packet came from and can send answers.

@Joachim_Pileborg is also right. It is not usually done.

If you need a specific interface, bind to it. If not, you probaply do not need it.

User
  • 14,131
  • 2
  • 40
  • 59
  • It almost works. `ifconfig` says there are two interfaces, `h1-eth0` w/IP address 10.0.0.3 and `lo` with 127.0.0.1. The snippet returns `('mininet-vm', [], ['127.0.1.1'])`. Perhaps this is the best I can do without resorting to ifconfig? I definitely see the problem and why I can never be sure (as you say, in case of NATs). – csl Apr 04 '14 at 11:14
  • Why is there no `"10.0.0.3"` in the listing of the I addresses? Now I am confused. I would have expected it to list all ip addresses. At least, it does under Windows. If you really want to find out which interface is used, you should comment that. Then the propability of additional answers is higher. – User Apr 04 '14 at 11:20
  • I ran it on the wrong host, but the only difference is that `ifconfig` returns 10.0.0.2 for the `h0-eth0` interface on the correct host (the one used in the question example). But the script still returns an empty address for that interface, it seems (exact same output as in the first comment). I'm running under mininet, which is a network simulator with a virtual network, but I don't think that should have anything to say as long as `ifconfig` works. – csl Apr 04 '14 at 11:26