robur-coop / miragevpn

An opinionated implementation of the OpenVPN protocol
BSD 2-Clause "Simplified" License
79 stars 9 forks source link

unify mirage-nat and mirage-router into a single unikernel where `--nat` is a runtime argument #253

Closed hannesm closed 6 months ago

hannesm commented 6 months ago

The code compiles, but a review would be nice to have. I suspect there's a bit more that we could share (the forward_or_reject comes to mind, which does ICMP errors -- and should be used in the NAT setting as well).

hannesm commented 6 months ago

This compiles fine, that is good news. What is left are actual tests of the unikernel, then we can merge :) There are as well some further possibilities to reduce the code size, and uniform the NAT and routing code paths -- as well as some TODO items in the code. But overall the merge of the unikernels lead to fewer code, and also cleaner code.

reynir commented 6 months ago

I tested this with --nat and it works for me - after adding the necessary routes and not trying from a different network on the private network side. I think we check on the private side that the source address is from the same subnet, and this may be why I can't ping or anything from the other machine.

I also tested without --nat, and I did not manage to get anything to work. This may very well be due to incorrect openvpn server configuration. I'm not sure how it's supposed to work.

hannesm commented 6 months ago

I think we check on the private side that the source address is from the same subnet, and this may be why I can't ping or anything from the other machine.

So, if we have a local network for the VPN (with NAT) - let's say 10.0.42.0/24, any packet from 10.0.42.0/24 should be NATted. I'm not sure what in this case "from the other machine" means IP-wise.

hannesm commented 6 months ago

I also tested without --nat, and I did not manage to get anything to work

I tested this case as well, and notice that any packet from the local network won't be forwarded over the tunnel. I'll dig into that.

reynir commented 6 months ago

I also tested without --nat, and I did not manage to get anything to work

I tested this case as well, and notice that any packet from the local network won't be forwarded over the tunnel. I'll dig into that.

A possible hint is the --iroute flag which informs OpenVPN about routes the client has. I don't know how it works, but it could be an explanation. I will be at the computer soon...

hannesm commented 6 months ago

I tracked it down to the Fragments.fragment API, which is not very intuitive. 82c4f0b fixes the issue at hand, and is deployed now as our as250 gateway :D

hannesm commented 6 months ago

Well, actually.. The Fragmens.fragment docstring is pretty previse what it expects (it is still unintuitive ;) -- what happens if we call it for the not-to-be-fragmented packet is that it returns a single packet, but puts an fragment offset into the IP header...

I've no clue why I chose such an API, we can track and improve on the mriage-tcpip repository if desired.

reynir commented 6 months ago

I think we check on the private side that the source address is from the same subnet, and this may be why I can't ping or anything from the other machine.

So, if we have a local network for the VPN (with NAT) - let's say 10.0.42.0/24, any packet from 10.0.42.0/24 should be NATted. I'm not sure what in this case "from the other machine" means IP-wise.

Let me explain in more detail my setup:

On host A (which runs the unikernel) I have a bridge for the unikernels and a currently disconnected ethernet interface with subnet 192.168.0.0/24. Host A also has a wifi card with subnet 192.168.1.0/24 and is setup to route traffic between the two (and maybe also some sort of NAT to allow internet access through the wifi card).

On host B it also has a bridge with ethernet for unikernels on subnet 192.168.0.0/24 but no further routing. It is also on the same wifi network with subnet 192.168.1.0/24. I manually add /32 routes to the unikernels on the other host via host A's wifi address, and this works fine for the stub resolver (and opam-mirror when I ran that until recently).

Finally, I tell host A and B to route 10.8.0.0/24 via the ovpn-router unikernel (192.168.0.250). On host A I can access 10.8.0.1, on host B there's no dice.

Writing all this down it's possible that the NAT on host A could be a cause of issues.

Edited to add: so the ovpn-router unikernel has as private network 192.168.0.250/24, and when host B connects from 192.168.1.x it's outside the private subnet, and I was suspecting this was the reason why things didn't seem to work.