socket_bind_addr

Published 08-14-2019 18:00:00

In this series of blog posts, I’d like to talk a little bit about socket programming in python, from the point of view of a network engineer.

I’ll start with how we should choose a source IP address.

Introduction

Python provides a thin wrapper around the system calls to the socket API. You can find the reference documentation here

The problem

When you are setting up a socket in general, you need to know where you will end up connecting to (destination). However you also need to choose a local IP to bind to, to create the socket.

In the networking world, this is relatively easy, as we just consult the routing table and see what is the IP of the egress interface for the destination (ie: netstat -rn, show route or platform equivilant).

In the programming world, this is less easy, as the socket API abstracts a lot of what the kernel does for you.

The Solution

Instead find the egress IP, or which IP we should bind to, we can setup a dummy UDP socket to our destination, and then see what IP the Kernel has picked for us. To do this, we’ll setup a socket with socket.socket() of Address Family INET (AF_INET), Protocol UDP (SOCK_DGRAM). We’ll then pass a tuple of destination address and port to socket.connect(). When we call connect, we dont actually do anything on the wire, since UDP is connection less.

With socket.getsockname(), we get a list back, with first entry being the source IP of the socket, then return this.

Code is as follows:

def egress_ip(ip_daddr):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect((ip_daddr, 1))  # connect() for UDP doesn't send packets
    ip_egress = s.getsockname()[0]
    return ip_egress