Silicondust / libhdhomerun

Silicondust library and cli utility for controlling HDHomeRun tuners
GNU Lesser General Public License v2.1
102 stars 43 forks source link

IP address selection misbehaviour #23

Open mercora opened 4 years ago

mercora commented 4 years ago

I usually tunnel all packets (even thus destined to the local subnet) via a separate gateway. This is done so all packets leaving this device get encrypted. The only packets that leave this device without being routed through this gateway are those packets that get the tunnel to the gateway working.

With this setup the library will insist on using the IP address of the untunneld interface. But packets get routed through the tunnel interface (like intended and according to the routing table and routing policies i have set up) but they will be silently dropped because the address is not allowed as source on this interface. If the interface through which i connect to the gateway is not on the same subnet as the device i want to connect to it just works like it should.

I don't know why this is the case but i suggest not trying to be too smart about the IP address selection and let the TCP/IP stack do its job of selecting an appropriate IP address while considering the local routing table and routing policies. If you really need to be explicit about the IP address selection it would be nice to consider the routing table or at least let users override the otherwise automatically selected IP address.

If there might be a reason this doesn't work as intended with regards to my setup i would be happy to get any hint on how to fix this. For now i setup a IP masquerading rule via iptables to rewrite the IP address to that of the outgoing interface but i would rather like to not do that.

tmm1 commented 4 years ago

With this setup the library will insist on using the IP address of the untunneld interface.

Are you talking about the discover command or something else?

mercora commented 4 years ago

When using the discover command packets leave the untunneld interface. This is because it is using multicast which wont work on the tunnel interface. Could this be the reason it will try to use this address? The hdhr device will reply on this interface with the locally configured IP address (the one that will later not be routed). I am about to wrap this library for use with Go and discovered this without using the discovery mechanism (at least not explicitly) but by explicitly setting the IP address on the call to hdhomerun_device_create.

tmm1 commented 4 years ago

If you want a pure-Go discovery use https://github.com/mh-orange/hdhomerun

mercora commented 4 years ago

While this would allow me to have more control on how the socket is created (i.e. by specifying an IP address) i think you should still consider to allow users of this library to do same. I guess now that you mentioned it, hdhomerun_device_create will still try to use the discovery even if an IP address has been given explicitly and use whatever address was used to receive the reply of the device. Whats interesting is that it will work when i am not connected through a network on the same subnet as the hdhr device. Does it skip discovery (only) in this case?

tmm1 commented 4 years ago

AFAIK the IP lookup stuff is only used during discovery due to multicast. If you specify an IP explicitly none of that stuff should happen.

mercora commented 4 years ago

I tried to follow the code path and it looks like in both cases the same code path is taken.

https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_device.c#L160 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_device.c#L37 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_device.c#L57 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_control.c#L49 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_control.c#L96 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_discover.c#L449 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_discover.c#L119 https://github.com/Silicondust/libhdhomerun/blob/master/hdhomerun_sock_posix.c#L40

this is where i lost it (read: i don't understand the code) but it sure looks to me as the same path is taken in either case. Please don't get me wrong i don't want to bother you too much about this and if you are confident this is an issue with my system i will accept this but otherwise i would like to at least understand this behavior.

tmm1 commented 4 years ago

It's possible I am mistaken. I am just another user of this library and did not write any of this code. I certainly don't have the unique network setup you do so I have not experienced the issue you are describing.

mercora commented 4 years ago

Ok understood, thanks for trying to help me :) I will try to debug this tomorrow some more with a fresh mind (and probably a debugger ;D).

nickkelsey commented 4 years ago

Hi, as of the most recent libhdhomerun the discover approach is to bind a socket to each local IP address found and send a global broadcast (255.255.255.255) from each one. This should send a broadcast packet out from each local IP address, including where there are multiple interfaces with isolated networks sharing an overlapping IP subnet as with AutoIP. Previous versions sent subnet broadcast packets which was required to work around the network stack in Windows XP.

The first thing that comes to mind is maybe the tunnel interface you are using is being filtered out for some reason. If you can step through things with a debugger it should be possible to check.

mercora commented 4 years ago

I did not yet take the time to debug this further but here is some clarification on what i think what it is that is happening here.

The tunnel interface is not capable of handling non unicast traffic. So no matter which approach is taken on my system the discovery process is not expected to work using this interface alone. To be clear, what i would like to do is to skip the discovery if i explicitly tell the library to which device it should connect to by specifying the address of the device explicitly.

Now what appears to happen when i am connected to the same subnet as the hdhr device is that discovery will actually work just fine and the device is found as it should be. This is because broadcast and multicast traffic is allowed to leave the plain untunneled ethernet interface. The problem i face is that after the device was discovered this way the library appears to be setting the source address explicitly to the address that received the discovery response. When the packet of the following connection attempt tries to leave this device the routing table is consulted and results in this packet being sent via the tunnel interface. This interfaces remote endpoint in turn has some strict rules about what source address packets are allowed to have and the address of the plain ethernet device that received the discovery reply packet is not one of them. If i am not connected to the same network as the hdhr device at all none of this happens. It looks like in this case the discovery is not even attempted and the library connects straight to the specified address as it is intended. Even if the code would attempt to try the discovery process it would fail as the hdhr device is not directly connected at all.

I understand that this is pretty specific to my setup and probably not very likely to happen on almost any other system. However, i think allowing users of this library to skip the discovery and directly connect to a specified address should be considered. There might also be some other reasons one wants to skip the discovery process too.