Work around IGDs that deduplicate entries based on the internal port

This is a violation of the UPnP IGD specification but we can relay through an alternate port as a workaround.
This commit is contained in:
Cameron Gutman
2020-08-10 21:16:21 -07:00
parent 5f015acdaa
commit 05413a554c
5 changed files with 148 additions and 0 deletions

View File

@@ -12,6 +12,7 @@
#include <assert.h>
#include <stdlib.h>
#include "relay.h"
#include "..\version.h"
#pragma comment(lib, "miniupnpc.lib")
@@ -205,6 +206,21 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
else if (indefinite) {
printf("STATIC ");
}
if (err == 718 && proto == IPPROTO_UDP) { // ConflictInMappingEntry
// Some UPnP implementations incorrectly deduplicate on the internal port instead
// of the external port, in violation of the UPnP IGD specification. Since GFE creates
// mappings on the same internal port as us, those routers break our mappings. To
// work around this issue, we run relays for each of the UDP ports on an alternate
// internal port. We'll try the alternate port if we get a conflict for a UDP entry.
// Given that these are already horribly non-spec compliant, we won't take any chances
// and we'll use an indefinite mapping too.
char altPortStr[6];
snprintf(altPortStr, sizeof(altPortStr), "%d", port + RELAY_PORT_OFFSET);
err = UPNP_AddPortMapping(
urls->controlURL, data->first.servicetype, portStr,
altPortStr, myAddr, myDesc, protoStr, nullptr, "0");
printf("ALTERNATE ");
}
if (err == UPNPCOMMAND_SUCCESS) {
printf("OK" NL);
return true;
@@ -852,6 +868,13 @@ int Run()
ResetLogFile();
// Create the UDP alternate port relays
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
if (k_Ports[i].proto == IPPROTO_UDP) {
StartUdpRelay(k_Ports[i].port);
}
}
// Create the thread to watch for GameStream state changes
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);