From: Florian Weingarten on
Hello,

I have a problem with the SO_BINDTODEVICE socket option. Maybe somebody
here can help me. Basically, I want to know how to FORCE the
transmission of some TCP/UDP packets via some specific interface,
even if the routing table says something different.

I have a system with a tun/tap interface and some specific network
is routed through that interface (via 'route add -net ... dev tun0').

I now want some userspace application (run as root) send packets
to a host in that network through the default route (which is
some gateway attached to eth0).

What I am doing so far is (basically):

* create the socket with socket()
* setsockopt(SO_BINDTODEVICE, eth0)
* bind() the socket to IN_ADDR_ANY and some port
* connect() to the remote host
* send() data

If the route through the tun0 interface is not there,
this works fine and the remote host receives the packets.
However, if the tun0 route is up, nothing is transmitted
through the default route (but nothing trough the tun0 route
either).

If I first start my client, THEN add the route, the transmission
is interrupted (not closed! there is just no data arriving). If
I delete the route again (while the client is running), the
server suddenly receives all data (that should have been
transmitted in between).

Any ideas what the problem might be?

Flo
From: David Schwartz on
On Dec 17, 11:52 am, Florian Weingarten <f...(a)go.cc> wrote:

> I have a problem with the SO_BINDTODEVICE socket option. Maybe somebody
> here can help me. Basically, I want to know how to FORCE the
> transmission of some TCP/UDP packets via some specific interface,
> even if the routing table says something different.

You cannot without using policy routing.

> Any ideas what the problem might be?

SO_BINDTODEVICE does not do what you think it does. Specifically, it
does *NOT* force outgoing UDP or TCP packets to take the "wrong"
interface, against the routing table.

If taking the right interface, according to the routing table, doesn't
work, then the routing table is wrong. You cannot fake around that
with anything but system-level policy routing.

Depending on your application, the right solution might also be to use
raw sockets or bpf.

DS
From: Florian Weingarten on
Hello David,

thank you for your answer!

David Schwartz <davids(a)webmaster.com> wrote:
> SO_BINDTODEVICE does not do what you think it does. Specifically, it
> does *NOT* force outgoing UDP or TCP packets to take the "wrong"
> interface, against the routing table.

Hm. Then maybe I misunderstood something. Some manpage I found
on the web says:

SO_BINDTODEVICE
Bind this socket to a particular device like "eth0", as specified
in the passed interface name. (...) If a socket is bound to an
interface, only packets received from that particular interface are
processed by the socket. Note that this only works for some socket
types, particularly AF_INET sockets

And some really old kernel docu I found says:

Once the BINDTODEVICE socket option has been set for a socket, as above,
any data sent over this socket is guaranteed to go out of the "eth1"
interface, and any data received through the socket is guaranteed to
have arrived on eth1. (...) Note that the routing table is still
consulted when packets are transmitted. Basically, routing proceeds
as usual, except that any routes which go through a network interface
other than the one specified in the BINDTODEVICE call are ignored.

> If taking the right interface, according to the routing table, doesn't
> work, then the routing table is wrong. You cannot fake around that
> with anything but system-level policy routing.

Well, to be concrete. What I want to do is to write a small tunnel
software (as a university exercise). I have a default route for some
network which goes to the tun0 interface, and the userspace program
which is associated with that tun0 interface (the "daemon") is then
supposed to send the raw packets, encapsulated as UDP packets, to
the destination host, but of course I cant just send them, because
then they would get delivered to the tun interface again (because
of the route for that net!). So I wanted something like "this
particular program should NOT use that route, all others should!".
SO_BINDTODEVICE sounded like a solution :-(

> Depending on your application, the right solution might also be to use
> raw sockets or bpf.

Why? Whats the difference? Does SO_BINDTODEVICE does what I want,
except it does not work with UDP/TCP, but with RAW sockets? Why is
that? Does that mean I have to use RAW sockets, then create my own
UDP packet (create the headers myself and stuff) and then it would
work?

Thanks for your time!

> DS

Flo
From: David Schwartz on
On Dec 18, 1:29 am, Florian Weingarten <f...(a)go.cc> wrote:

> Hm. Then maybe I misunderstood something. Some manpage I found
> on the web says:

Definitely.

> SO_BINDTODEVICE
>     Bind this socket to a particular device like "eth0", as specified
>     in the passed interface name. (...) If a socket is bound to an
>     interface, only packets received from that particular interface are
>     processed by the socket. Note that this only works for some socket
>     types, particularly AF_INET sockets

This is mostly correct. Note that "received from that particular
interface" does not mean what you think it means. Specifically, this
does not mean that if you bind to an Ethernet interface, only packets
received over the wire connected to that physical interface will be
processed by the socket.

> And some really old kernel docu I found says:
>
>  Once the BINDTODEVICE socket option has been set for a socket, as above,
>  any data sent over this socket is guaranteed to go out of the "eth1"
>  interface, and any data received through the socket is guaranteed to
>  have arrived on eth1. (...) Note that the routing table is still
>  consulted when packets are transmitted. Basically, routing proceeds
>  as usual, except that any routes which go through a network interface
>  other than the one specified in the BINDTODEVICE call are ignored.

This is not correct for TCP or UDP. It is correct for raw.

> > If taking the right interface, according to the routing table, doesn't
> > work, then the routing table is wrong. You cannot fake around that
> > with anything but system-level policy routing.

> Well, to be concrete. What I want to do is to write a small tunnel
> software (as a university exercise). I have a default route for some
> network which goes to the tun0 interface, and the userspace program
> which is associated with that tun0 interface (the "daemon") is then
> supposed to send the raw packets, encapsulated as UDP packets, to
> the destination host, but of course I cant just send them, because
> then they would get delivered to the tun interface again (because
> of the route for that net!). So I wanted something like "this
> particular program should NOT use that route, all others should!".
> SO_BINDTODEVICE sounded like a solution :-(

The usual way is to make a more specific route for the other end of
the tunnel. Alternatively, you can use policy routing. As is, your
routing table is simply disastrously wrong, it has an implosion route
for the other end of the tunnel. Nothing you can do to one particular
socket can fix the fact that your routing table is wrong.

> > Depending on your application, the right solution might also be to use
> > raw sockets or bpf.

> Why? Whats the difference? Does SO_BINDTODEVICE does what I want,
> except it does not work with UDP/TCP, but with RAW sockets? Why is
> that?

Because the kernel's implementation of UDP/TCP has to have certain
assumptions for its own sanity. One of them is that the routing table
does not contain fatal defects such as implosion routes. If you want
to use the kernel's UDP/TCP, you must make sure the assumptions it
relies on are in fact correct. One of them is that routing table
doesn't contain routes that fail horribly.

> Does that mean I have to use RAW sockets, then create my own
> UDP packet (create the headers myself and stuff) and then it would
> work?

You could do it that way. The preferred way is either a more-specific
route for the other end of the tunnel or not using UDP as the
encapsulation protocol.

DS