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
PMTUD may be implemented incorrectly #3327
Comments
I also wrote a custom PacketConn that adds some padding bytes to every packet to make it larger, and passed it to quic-go. The QUIC payload reaches MaxPacketBufferSize regardless of the MTU and the actual packet size going out. |
Does setting the DF socket option resolve this? |
That would lead to the same problem as in #3273 - the kernel returns error instead of silently dropping the packet, at least on Linux. Haven't tried it on any other platform. |
The error looks like this on Linux: |
We can either catch that error, or just ignore it and let loss recovery kick in after an RTT or so. This is not the main purpose of PMTUD anyway. You really shouldn't be sending packets larger than your local link MTU anyway. The more interesting use for PMTUD is finding the MTU of a path over the internet, for which the current code still works fine (or doesn't it)? |
I'm not sure how the current code handles this error, I'll have a look tomorrow. But I can confirm that this error does NOT only occur when sending a packet larger than the local MTU. It occurs when sending a packet larger than the path MTU. |
How does the kernel know about the path MTU in the case of UDP? |
No it doesn't. quic-go doesn't do anything about DF bit on Linux right now. Since by default it's IP_PMTUDISC_WANT, Linux DOES fragment and send the probe packets. |
|
I'm not sure how the kernel would do that for UDP. There's no acknowledgements, so the kernel can't know if a packet of a certain size has been delivered (ICMP can't be relied upon on the internet). |
But ICMP does work in most cases. I assume that's how Linux implements it (RFC 1191) |
Then the right solution would be to ignore this error message. PMTUD packets don't carry any application data, so there's no benefit in declaring them lost right away. If we wanted to be fancy, we could tell the MTU discoverer right away, but I'm not sure if that's worth the extra complexity. Do you want to send us a PR? |
Tried this code on a VPS with a local MTU of 9000 but a path MTU of 1500. package main
import (
"fmt"
"golang.org/x/sys/unix"
"net"
)
func main() {
conn, err := net.ListenUDP("udp", nil)
if err != nil {
panic(err)
}
defer conn.Close()
rc, err := conn.SyscallConn()
if err != nil {
panic(err)
}
err = rc.Control(func(fd uintptr) {
err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO)
if err != nil {
panic(err)
}
})
if err != nil {
panic(err)
}
bs := make([]byte, 7000)
fmt.Println(conn.WriteToUDP(bs, &net.UDPAddr{
IP: net.IPv4(8, 8, 8, 8),
Port: 53,
}))
} First packet went through. Subsequent attempts all returned errors. |
Maybe 🤔 I'll see what I can do |
I'm too busy with school to dive into this. If you are able, would greatly appreciate your effort! |
@marten-seemann Are we ignoring all errors from bs := make([]byte, 7000)
_, err = conn.WriteToUDP(bs, &net.UDPAddr{
IP: net.IPv4(8, 8, 8, 8),
Port: 53,
})
if opErr, ok := err.(*net.OpError); ok {
if syscallErr, ok := opErr.Err.(*os.SyscallError); ok {
if syscallErrno, ok := syscallErr.Err.(syscall.Errno); ok {
if syscallErrno == unix.EMSGSIZE {
// This is what we are looking for.
fmt.Println("EMSGSIZE")
}
fmt.Println(syscallErrno)
fmt.Println(syscallErrno.Error())
}
}
} This is undocumented and platform-specific. |
I think it would be nicer to just ignore the MTU-related ones. At least that would be a requirement if we want to feed the information back into the MTU discoverer immediately. Your code might become a bit more elegant if you use |
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, this commit re-enables MTU discovery on Windows unless it is disable explicitly by user. Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, this commit re-enables MTU discovery on Windows unless it is disabled explicitly by user. Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, this commit re-enables MTU discovery on Windows unless it is disabled explicitly by user. Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, this commit re-enables MTU discovery on Windows unless it is disabled explicitly by user. Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, this commit re-enables MTU discovery on Windows unless it is disabled explicitly by user. Undo quic-go#3276 Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo quic-go#3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo quic-go#3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo quic-go#3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo quic-go#3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes quic-go#3273 quic-go#3327
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo #3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes #3273 #3327
This was fixed in #3328. |
This commit introduces additional platform-dependent checking when the kernel returns an error. Previously, the session is terminated when PingFrame sends a discovery packet larger than the limit. With this commit, the error is checked, and if it is "datagram too large", the error is ignored. Additionally, 1. This commit re-enables MTU discovery on Windows unless it is disabled explicitly by user (Undo quic-go#3276), 2. Set IP_DONTFRAGMENT and IPV6_DONTFRAG with error checking on Windows, and 3. Set IP_MTU_DISCOVERY to PMTUDISC_DO for both IPv4 and IPv6 on Linux so that the kernel will return "message too long". Fixes quic-go#3273 quic-go#3327
Hi,
We talked about the DF bit on Windows a while ago, and I submitted #3155. But apparently it led to this unforeseen problem #3273 so PMTUD ended up being disabled on Windows as a dirty fix. As I revisited this today I realized that perhaps there is something fundamentally wrong with the way quic-go currently implements PMTUD, and it's not really a Windows issue.
Previously we both assumed that the DF bit is enabled by default on Linux. But that's not true, as the man page says this:
The default behavior on Linux (
IP_PMTUDISC_WANT
) is to only set DF bit when the packet is smaller than the path MTU, and fragment the packet otherwise. This implies that right now even if quic-go sends a packet larger than the path MTU, it will still be fragmented, sent, and received, nullifying PMTUD altogether. And if you explicitly set it toIP_PMTUDISC_DO
, you will get a similar behavior as on Windows - errors when trying to send packets larger than the path MTU, instead of discarding them silently.I wrote a demo and verified this on my Ubuntu 20.04 server, which has an MTU of 1500. When it sends packets smaller than 1500, the DF bit is set. When it sends packets larger than 1500, it fragments them and sends them successfully nonetheless.
The text was updated successfully, but these errors were encountered: