Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public class HyProxyConfiguration {
private Map<String, String> backends;
private Map<String, List<String>> permissions;

// QUIC UDP datagram sizing. maxUdpPayloadSize caps datagrams in both directions so
// they survive the path MTU; discoverPmtu toggles DPLPMTUD probing. See MHPL-615:
// both are tuned down when the proxy sits behind Cloudflare Spectrum, which drops
// oversized/fragmented UDP instead of forwarding it.
private int maxUdpPayloadSize;
private boolean discoverPmtu;

public InetSocketAddress getBind() {
return AddressUtil.parseAndResolveAddress(bind);
}
Expand Down Expand Up @@ -184,6 +191,9 @@ public static HyProxyConfiguration load(HyProxy proxy, Path configFilePath) thro
String initialBackend = config.getOrElse("initial-backend", "main");
boolean proxyCommunicationEnabled = config.getOrElse("proxy-communication", true);

int maxUdpPayloadSize = config.getIntOrElse("max-udp-payload-size", 1200);
boolean discoverPmtu = config.getOrElse("discover-pmtu", false);

CommentedConfig backendConfig = config.get("backends");
Map<String, String> backends = backendConfig.valueMap()
.entrySet()
Expand All @@ -208,7 +218,9 @@ public static HyProxyConfiguration load(HyProxy proxy, Path configFilePath) thro
initialBackend,
proxyCommunicationEnabled,
backends,
permissions
permissions,
maxUdpPayloadSize,
discoverPmtu
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jspecify.annotations.Nullable;
import ac.eva.hyproxy.HyProxy;
import ac.eva.hyproxy.common.util.ProtocolUtil;
import ac.eva.hyproxy.config.HyProxyConfiguration;
import ac.eva.hyproxy.io.channel.InboundChannelInitializer;
import ac.eva.hyproxy.io.proto.DisconnectType;

Expand All @@ -33,6 +34,9 @@ public boolean isSharable() {

@Override
public void channelActive(ChannelHandlerContext ctx) {
HyProxyConfiguration config = this.proxy.getConfiguration();
int maxUdpPayloadSize = config.getMaxUdpPayloadSize();

ChannelHandler handler = new QuicServerCodecBuilder()
.sslContext(this.sslContext)
.tokenHandler(InsecureQuicTokenHandler.INSTANCE)
Expand All @@ -45,7 +49,15 @@ public void channelActive(ChannelHandlerContext ctx) {
.initialMaxStreamDataBidirectionalLocal(128 * 1024)
.initialMaxStreamDataBidirectionalRemote(128 * 1024)
.initialMaxStreamsBidirectional(8)
.discoverPmtu(true)
// MHPL-615: cap the QUIC datagram size so packets survive the path MTU.
// maxRecvUdpPayloadSize is advertised to the client as our
// max_udp_payload_size, forcing it to never send us datagrams larger than
// this (the inbound direction that Cloudflare Spectrum blackholes); the
// send cap bounds our outbound datagrams. discoverPmtu is off behind
// Spectrum so DPLPMTUD does not grow datagrams past the forwardable size.
.maxRecvUdpPayloadSize(maxUdpPayloadSize)
.maxSendUdpPayloadSize(maxUdpPayloadSize)
.discoverPmtu(config.isDiscoverPmtu())
.congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR)
.handler(new ChannelInboundHandlerAdapter() {
@Override
Expand Down
17 changes: 17 additions & 0 deletions proxy/src/main/resources/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ proxy-secret-file = "proxy.secret"
# if we should bind on ipv6 too, heavily recommended
ipv6-support = true

# maximum QUIC UDP payload size (bytes) used with clients. this caps the datagram
# size in BOTH directions: it is advertised to the client as our max_udp_payload_size
# (so the client never sends us anything larger) and it bounds our own outgoing
# datagrams. keep it small enough to survive the whole path MTU.
# IMPORTANT (MHPL-615): Cloudflare Spectrum does NOT fragment UDP and silently drops
# any datagram too large to forward, so when the proxy is behind Spectrum this must
# stay under the Spectrum edge->origin forwardable size. 1200 is QUIC's universal
# floor (always deliverable); raise toward 1350 to recover throughput once stability
# is confirmed. must be >= 1200.
max-udp-payload-size = 1200

# whether QUIC should probe for a larger path MTU (DPLPMTUD). keep this off behind
# Cloudflare Spectrum: probing grows datagrams past the Spectrum-forwardable size and,
# with ICMP "packet too big" filtered on anycast paths, those packets get blackholed.
# only enable for direct (non-Spectrum) UDP exposure where the path MTU is trustworthy.
discover-pmtu = false

# initial backend or backend set we should in order try to connect the user to.
initial-backend = "main"

Expand Down