-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Get it working with IPv6 #75
Comments
This issue was also described here: https://dev.wlan-si.net/ticket/1187 For few years now we had it for Google Summer of Code. But it is pretty tricky to get it right. I think it will require a kernel modification. The issue is that we are currently using NAT to get all connections to the same port. You probably do not want to use this with IPv6, but would want that Linux kernel finally identifies connections based on 4-tuple of src port, src ip, dst ip, dst port. Implementing this would even make IPv4 implementation simpler. |
I am not familiar with the status/outcome of the GSOC2017. But if i assume that it is not "as simple as expected": |
There was no working outcome. Students didn't manage to do it. You can propose what that approach would be? |
the most nasty approach would be: Setup locally on the client a 4to6 and on the server a 6to4 gateway, linked static and route explicitly through this routing table for the tunneldigger/L2TP. |
What we could try is porting tunneldigger to (ab)using vxlan instead of L2TPv3 for tunneling. |
@kaechele VXLAN does not work over NAT (and stateful firewalls) at all, as it uses random source ports (based on flow hashs), so packets in opposite directions aren't associated with each other. Generally, I consider VXLAN unfit for use over WAN networks. I don't really know enough about tunneldigger to make an informed suggestion, but maybe someone can answer my question: Why does tunneldigger use a fixed port for the tunnel at all, thus requiring the NAT hack? Wouldn't it be much simpler and saner to use a distinct port for each tunnel like L2TP is meant to be used (only using a fixed port for the broker connection to coordinate the setup of the L2TP tunnel)? It would also make connections more robust over bad NAT routers (In fastd development, we've observed the following two issues: 1) Broken NAT routers not allowing UDP flows from the same host/port to multiple peers. 2) Broken NAT routers not allowing packets to flow at all or only with tiny MTU for previously known UDP flows after the uplink connection was reestablished. Both issues have been mitigated by using a separate UDP socket for each peer, and creating new sockets with new ports whenever the connection is lost). |
Just to be clear. L2TP is meant to be used over same port, this is why every package has a session ID even. (And even if it there would be no session ID, you could also have unique tunnels because not two tunnels would share a tuple of (client IP, client port, server IP, server port). But that would break abstraction between tunneling and transport.) The Linux implementation has this crazy limitation. Not sure why.
So that it is easy to open necessary ports on firewalls. Also in our network, we use port 53 (DNS) which is generally always open. Other ports tend to be closed much more. UDP is not connection-based, so you need ports to be open in both directions. Many cheap home routers do not use support UDP based stateful firewalling.
We could add this to Tunneldigger, it can probably be a simple configuration switch. But do not complain about closed ports and connections not being established. Maybe on IPv6 there will be less of that. I have not seen so much port closing on IPv6. Maybe because it is even cheap to have one IP per tunnel. So instead of NAT, on IPv6, we could have one IPv6 address per tunnel, with same port on all IPs.
Just to be clear. NAT happens on the server, not on the client. It has nothing with client.
Your client host/port can be anything, it does not have to be fixed. It probably should not be fixed. And hopefully you are not running a VPN server behind (broken) NAT?
What do you mean by "connection"? UDP flows do not have connections? BTW, Freifunk was accepted again to GSoC so this issue is again open this year to be resolved. If anyone of you is student, I would encourage you to apply and get payed to fix this issue. :-) |
FWIW, we tell users that they need to allow communication on a certain set of UDP ports (10000-10010) to enable Freifunk. This is relevant e.g. when people run Freifunk routers inside a "guest network" like some modern home routers provide it, that has very restricted internet access. We also have some Freifunk routers in a network that heavily traffic shapes UDP on all ports except 53. So, we do rely on all tunneldigger+l2tp communication going over a single UDP port on the server side. This was also the case when we used fastd, so @NeoRaider are you describing changes to fastd to no longer just have a single server-side port? |
@RalfJung the discussion was/is mainly about outgoing ports, not server side incoming ports, as far as i understand. |
Now that you say this, I realize that I assumed that on the server side, the outgoing port is the same as the incoming port. However, I assume for the guest network firewalls, incoming packages also have to come from whitelisted port, so the outgoing serverside port would also have to be fixed. |
Thanks for the explanation. I'd say, let's fix the kernel, so eventually no workarounds are necessary for this limitation anymore.
Maybe the term "session" would be more appropriate. What a fastd handshake establishes on top of an UDP flow. Not in any way relevant to the tunneldigger discussion though...
Random source ports are the usual case with most software. Firewalls are stateful, so as long as the outgoing port to the server is open, the opposite direction will just work (no need to change anything here). If your firewall requires a fixed source port, replace the firewall, IMO it is really not worth the effort to deal with such systems. |
I am confused, I feel we are not talking about the same thing. I said "serverside sourceport"; of course the clientside sourceport is random -- but the sourceport of the server->client packets will be the same as the destport of the client->server packets (and hence be fixed, e.g. 10000 in our case), right? Even UDP is usually used bidirectionally? I don't even see a good reason for doing anything else. |
@RalfJung After understanding that using different ports for different tunnels is a limitation of the Linux kernel and not of the L2TP standard, I don't think different server ports should be used. (fastd doesn't work any different, and no changes are planned; I had only suggested such a change for tunneldigger because of my insufficient knowledge of L2TP) |
Here is a relevant standard section: https://tools.ietf.org/html/rfc3931#section-4.1.2.2 @NeoRaider Just to be clear. This is my understanding of the protocol and standard. I might be mistaken and that there exist some reasonable way why it is done like this in Linux. But to my understanding this is unnecessary limitation and standard has a session ID one could use to differentiate packets coming over the same port. About discussion above about ports. To my understanding, the main improvement on prevention of blocking is that traffic going from a client (a device in home network of a user behind a cheap router) going to a server has to have a common and fixed destination port (like 53 UDP port). Source port (port used by a client) can be random. Even more, for secure DNS queries it has to be random. This is one of main mitigation techniques used to increase entropy in DNS queries. I cannot believe that even a cheap router would prevent random source ports for DNS queries. But destination port (port on the server) should be fixed. So that traffic looks like DNS. This is what NAT is doing on the server (an ugly workaround). NAT is using the above mentioned tuple to know how to map ports. It does not use session ID. But Linux implementation could just be using session ID. But for IPv6 we might not need this restriction. Server could have /64 of IPv6 addresses and each tunnel could go to a different address, same fixed 53 UDP port. So maybe this is a workaround. But it would be still ugly. This means that server would have to listen on /64 IP addresses. I am not even sure this would be performant. |
I'm pretty sure not all the servers we run and that have IPv6, have more than a single (/128) address assigned to them. If you rent a VPS or similar systems, you don't typically get an entire subnet. |
You don't? I found it very easy and cheap (free) to ask them to give you more. They currently do that easily. See here. So DigitalOcean allocates to you a range, but it does not configure it to you by default. |
To be fair, we never asked. |
I don't know if I'm totally misunderstanding how the kernel and sockets work here but it seems that the kernel does differentiate based on tunnel and session id (see l2tp_core.c at 868-888) when reading packets. |
Okay. Did some more analysis.
The problem here is that in the current code the socket is used to infer context as to which tunnel the data messages belong to and then goes on to infer session context (the actual relevant context) from the session ID read from the data messages. Technically the driver is already set up to enforce uniqueness of session IDs not only for L2TPv3 but also for L2TPv2 which doesn't specifically require this.
Technically the driver does that already: https://github.com/torvalds/linux/blob/master/net/l2tp/l2tp_core.c#L874 But the l2tp_tunnel struct is still used as a kind of glue between the kernel and all sessions. Specifically for all the cleanup that takes place when a tunnel gets torn down. So from my understanding the kernel driver needs to be modified to basically just look at the data messages and infer context from the session ID alone. Control messages are directly passed to userspace anyway, so there's not really a point of dealing with them in kernelspace anyway. I have also locally built a test version of the broker based on socketserver and threading (rather than raw sockets and the epoll event loop). But it's super ugly and was just for my personal testing to see if I could easily get at least the broker protocol to work on IPv6 (the answer is yes, but only to then hit the kernel brick wall). It changes so much code (since I didn't really know what's going on with this whole epoll stuff) that it's really more of a rewrite than an evolution, so I'm somewhat unwilling to share. But boy. The code is a lot lighter without that NAT foo baked in. |
It would be awesome, if you shared your code. As R. C. Martin says in ”Clean Code“: The way to clean code leads over bad code ;) |
I verified my understanding of the situation with James Chapman, the original author of the Kernel L2TP implementation. He was so kind to take the time and provide me some insight. |
Yes, this was also my understanding. We have to fix the kernel to get this to work properly without NAT hack. But given that mostly we run this on devices where we do control kernel, I think this could be fixed with a patch even if it does not get into upstream. Moreover, Batman people I think have quite some experience getting things into upstream kernel so we could work with them. Or at least get this as a patch into OpenWrt kernels. |
Let me break out what needs to be done in order to get this done:
|
Yes, this fix in the kernel would then also remove need for NAT for IPv4. |
It would be really great to be able to use native IPv6 on the WAN side, since more and more broadband-consumers get "IPv6 + DSlite".
Meaning that IPv4 is only "via CarrierGradeNat/CGN", where we have to observe very often strange effects like dying connections and/or packet loss. While IPv6 is working fine.
As far as i understand, it's not a simple job like for IPv4, since the packet header sizes are not that static in V6, but it would be really helpful, even it would end up as a total rewrite.
The text was updated successfully, but these errors were encountered: