52

If a computer has multiple network cards, all of them connected to different networks and functioning properly, when we open a socket, how does the OS determine which NIC to use with this socket? Does the socket API allow us to explicitly specify the NIC that is to be used?

neo
  • 791
  • 5
  • 7

4 Answers4

56

I'm writing this from a Linux perspective, but I suppose it applies everywhere.

The decision is made when the socket is bound. When bind is called, the address you specify determines the interface the socket will listen on. (Or even all interfaces.)

Even if you don't use bind, it happens implicitly when you connect. The destination is looked up in the route table, which must contain a route to the destination network. The route also contains the interface to use and can optionally even specify the source address. If no source address is specified, the primary address of the interface is taken.

You can actually use bind together with connect, to force your outgoing connection to use a specific address and port. A socket must always have these two bits of information, so even when you don't, the primary address is used and a random port are chosen.

Stéphan Kochen
  • 19,513
  • 9
  • 61
  • 50
  • 1
    "When bind is called, the address you specify determines the interface the socket will listen on." How does the `bind` determine this? What if you have several interfaces on the same subnet? – flow2k Jul 05 '19 at 19:04
  • What if I have bind my socket to an ip address which doesn't match with any of the interfaces? Then is there any NAT done by Kernel? – abhiarora Mar 16 '20 at 12:09
  • If I bind an interface before to connect, Does that mean the connect for outgoing traffic will use that interface I bind without following the routing decision? – nuclear Jul 27 '21 at 09:04
  • 1
    @flow2k when you `bind()` to an IP, you are binding to the specific interface that the IP belongs to. You can't have multiple interfaces with the same IP. – Remy Lebeau Jul 27 '21 at 14:29
  • @abhiarora if the IP doesn't exist, the `bind()` will fail. – Remy Lebeau Jul 27 '21 at 14:30
35

I dont know why im included in the edit suggestion when i was not even related to this question .I got similar edit suggestion before as well..might be some bug/issue.

(If you feel inclined to up-vote, @Shtééf's answer deserves it more than mine.)

That depends on whether you are connecting or binding.

If you bind, you can bind to a specific IP address corresponding to one of the machine's interfaces, or you can bind to 0.0.0.0, in which case the socket will listen on all interfaces.

If you connect an unbound socket, then the machine's routing tables, in conjunction with the destination IP adress, will determine which interface the connection request goes out on.

It is possible to bind a socket then connect it. In this case, the socket will remain bound as per the bind call when it makes the connection. (Thanks to @RemyLebeau for pointing this out.)

Krishna
  • 7,290
  • 3
  • 25
  • 25
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • 1
    Unless you call bind() before calling connect(). – Remy Lebeau Nov 29 '10 at 21:21
  • @RemyLebeau Actually that's not necessarily true. There's a long discussion in one of the TCP RFC's about 'strong and weak end system models', and the conclusion is that TCP only requires the 'weak end' model. What you say is only true of the 'strong end' model. – user207421 May 24 '13 at 00:54
  • 1
    I don't know what "weak end" and "strong end" refer to, but every platform I know of that supports sockets allow `bind()` to be called before `connect()` when a client needs to use a specific IP/adapter for the outbound connection. If `bind()` is not called beforehand, `connect()` decides which one to use. – Remy Lebeau May 24 '13 at 13:36
  • 1
    @RemyLebeau I wasn't clear. The part that isn't necessarily true is the implication that if you bind to a specific interface, the connection request will go out on that interface. The weak-end system model allows that not to be the case. If you don't know what the strong and weak-end system models are, you could always look them up. In the RFCs. – user207421 May 25 '13 at 10:29
  • 1
    If you bind to an interface before connecting, the local address of the connection is bound, the connection request HAS to use that interface, or it violates the binding. – Remy Lebeau May 25 '13 at 17:31
  • @RemyLebeau If I bind an interface before to connect, Does that mean the connect for outgoing traffic will use that interface I bind without follow the routing decision? – nuclear Jul 27 '21 at 09:02
  • @nuclear yes, if you `bind()` to an interface before `connect()`'ing, the connection will go out through that interface. That is the whole point. – Remy Lebeau Jul 27 '21 at 14:32
  • @RemyLebeau but according to the docs https://linux.die.net/man/7/socket said, SO_BINDTODEVICE only impact inbound traffic, not outgoing packet `If a socket is bound to an interface, only packets received from that particular interface are processed by the socket.` – nuclear Jul 28 '21 at 06:09
  • @RemyLebeau https://stackoverflow.com/a/33921305/7529562 author said `no matter which interface the socket is bound, the packet is to be routed based on route table.` Does he is wrong? – nuclear Jul 31 '21 at 10:45
2

I'm not really sure which method is the best, but there is an alternative theory to the bind()-before-connect() approach that Shtééf presented. It's to use setsockopt() with SO_BINDTODEVICE . See: http://codingrelic.geekhold.com/2009/10/code-snippet-sobindtodevice.html

Ph0t0n
  • 970
  • 9
  • 10
1

As an alternative, you can search for the appropriate nic based on its name:

        //Find the ip address based on the ethernet adapter name. On my machine the ethernet adapter is "Ethernet"
        System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
        System.Net.NetworkInformation.NetworkInterface ethernet = nics.Where(n => n.Name.Equals("Ethernet")).Single();
        UnicastIPAddressInformation uniCastIPAddressInformation = ethernet.GetIPProperties().UnicastAddresses.Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Single();

        IPEndPoint localEndPoint = new IPEndPoint(uniCastIPAddressInformation.Address, 9000);

        //Create a TCP/IP socket.  
        Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //Bind and start listening
        listener.Bind(localEndPoint);
        listener.Listen(10);
Bart De Boeck
  • 642
  • 6
  • 9