Linux: How do I force a specific network interface to be used?

后端 未结 4 1124
被撕碎了的回忆
被撕碎了的回忆 2021-01-31 10:55

This could be considered a continuation of this earlier SO question.

Ideally, I\'d like to jail a process into only using a certain interface, no matter what. It will be

4条回答
  •  温柔的废话
    2021-01-31 11:00

    As for my general question, there appear to be a few ways to do it:

    • The complicated way that involves the routing table changes and cooperation from each process. This is the way I described above. One advantage it has it it works from userspace. I have put some additional notes on it and answered my specific questions below.

    • Write a custom kernel model that ignores the routing table completely, if SO_BINDTODEVICE is set. The client process is still required to call setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev), however. This option is definitely not for the faint of heart.

    • Virtualize the process. This probably isn't appropriate for a lot of people, and it will brings own set of headaches, mostly with configuration. But it is worth a mention.

    Options 1 and 2 require processes to opt-in to work as we'd like them to. This can be partially alleviated by creating a dynamic library that hijacks the socket() call to create the socket and then immediately bind it to the device before returning the descriptor. This is described in more detail here.

    After doing some research and lots of Googling, I can draw a few conclusions on how the Linux kernel 2.6.26 behaves. Note that these are probably all implementation-specific behaviors, and perhaps even kernel-specific. Test your own platform before deciding to implement features based upon my single point of data.

    1. SO_BINDTODEVICE does indeed do what it says, at least for UDP.

    2. Unique IPs for each interface seems to be necessary, since we're using the routing tables. A custom kernel module could bypass this restriction.

    3. To receive broadcasts on a specific interface, bind to the device first using SO_BINDTODEVICE, and then bind to the broadcast address with the usual bind() call. The device binding needs to be done before anything else. Then, the socket will receive only broadcasts that arrive on that interface.

    I tested it by first creating a socket. Then I bound it to a specific interface using setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev). Finally, I bound it to the broadcast address. From another computer, I sent a broadcast that would be received through a nonbound interface. The device-bound socket did not receive this broadcast, which makes sense. Remove the setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev) call and the broadcast will be received.

    It should also be mentioned that you can use setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) here as well. Note that the semantics of SO_REUSEADDR change with broadcast addresses. Specifically, it is legal to have two sockets bound to the broadcast address and the same port on the same machine if they both have SO_REUSEADDR set.

    UPDATE: SO_BINDTODEVICE with broadcasts seems to be fraught with peril, specifically, reception of broadcast frames. I was observing broadcast frames received on one interface, but disappearing on the other at the same time. It appears that they are effected by the local routing table, but are immune to IP policy rules. However, I'm not 100% certain about this and am only mentioning it as a point of investigation if you wish to continue it. All this to say: use at your own risk. In the interest of time, I opened a raw socket on the interface and parsed the Ethernet and IP headers myself.

提交回复
热议问题