Last active
February 21, 2026 04:01
-
-
Save clayfreeman/9507314e3c7e0472c5ee8e66e01e966c to your computer and use it in GitHub Desktop.
Puts a synthetic upstream load on your internet connection at the specified bitrate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @file | |
| * Put a synthetic upstream load on your internet at the specified bitrate. | |
| * | |
| * Compile with your preferred TARGET_HOSTNAME and TARGET_PORT: | |
| * - cl /DTARGET_HOSTNAME=\"example.internal\" /DTARGET_PORT=62724 udpsink.c | |
| * - x86_64-w64-mingw32-gcc -DTARGET_HOSTNAME=\"example.internal\" -DTARGET_PORT=62724 udpsink.c -o udpsink.exe -lws2_32 | |
| */ | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <winsock2.h> | |
| #include <windows.h> | |
| #include <mswsock.h> | |
| #include <ws2tcpip.h> | |
| #pragma comment(lib, "ws2_32.lib") | |
| #ifndef TARGET_HOSTNAME | |
| #define TARGET_HOSTNAME "" | |
| #endif | |
| #ifndef TARGET_PORT | |
| #define TARGET_PORT 0 | |
| #endif | |
| #define TOTAL_IP_SIZE 1280 | |
| #define IPV4_HEADER 20 | |
| #define UDP_HEADER 8 | |
| #define PAYLOAD_SIZE (TOTAL_IP_SIZE - IPV4_HEADER - UDP_HEADER) | |
| #define BATCH_SIZE 10000 | |
| #define BASE_INTERVAL_MS 10 | |
| static volatile uint64_t bytes_sent_report = 0; | |
| static volatile uint64_t bytes_per_sec = 0; | |
| static LARGE_INTEGER freq; | |
| DWORD WINAPI sender_thread(LPVOID param) | |
| { | |
| SOCKET sock = ((SOCKET *) param)[0]; | |
| LPFN_TRANSMITPACKETS TransmitPackets = ((LPFN_TRANSMITPACKETS *) param)[1]; | |
| TRANSMIT_PACKETS_ELEMENT *elements = ((TRANSMIT_PACKETS_ELEMENT **) param)[2]; | |
| LARGE_INTEGER last_time; | |
| QueryPerformanceCounter(&last_time); | |
| uint64_t leftover_bytes = 0; | |
| while (1) | |
| { | |
| LARGE_INTEGER now; | |
| QueryPerformanceCounter(&now); | |
| double elapsed_sec = (double) (now.QuadPart - last_time.QuadPart) / freq.QuadPart; | |
| last_time = now; | |
| uint64_t target_bytes = bytes_per_sec * elapsed_sec + leftover_bytes; | |
| leftover_bytes = 0; | |
| while (target_bytes >= TOTAL_IP_SIZE) | |
| { | |
| uint64_t packets_to_send = (target_bytes / TOTAL_IP_SIZE); | |
| if (packets_to_send > BATCH_SIZE) | |
| { | |
| packets_to_send = BATCH_SIZE; | |
| } | |
| if (!TransmitPackets(sock, elements, packets_to_send, 0, NULL, 0)) | |
| { | |
| printf("TransmitPackets() error: %d\n", WSAGetLastError()); | |
| break; | |
| } | |
| uint64_t sent = packets_to_send * TOTAL_IP_SIZE; | |
| target_bytes -= sent; | |
| InterlockedAdd64((volatile LONG64 *) &bytes_sent_report, sent); | |
| } | |
| leftover_bytes = target_bytes; | |
| Sleep(BASE_INTERVAL_MS); | |
| } | |
| return 0; | |
| } | |
| DWORD WINAPI reporter_thread(LPVOID param) | |
| { | |
| LARGE_INTEGER last_report; | |
| QueryPerformanceCounter(&last_report); | |
| while (1) | |
| { | |
| Sleep(3000); | |
| LARGE_INTEGER now; | |
| QueryPerformanceCounter(&now); | |
| double elapsed_report_sec = (double) (now.QuadPart - last_report.QuadPart) / freq.QuadPart; | |
| uint64_t sent = InterlockedExchange64((volatile LONG64 *) &bytes_sent_report, 0); | |
| SYSTEMTIME st; | |
| GetLocalTime(&st); | |
| double mbps_actual = (sent * 8.0) / (elapsed_report_sec * 1e6); | |
| printf("[%02d:%02d:%02d] Actual bitrate: %.2f Mb/s\n", st.wHour, st.wMinute, st.wSecond, mbps_actual); | |
| last_report = now; | |
| } | |
| return 0; | |
| } | |
| int main() | |
| { | |
| uint64_t mbps = 0; | |
| printf("Bitrate (Mb/s): "); | |
| if (scanf("%llu", &mbps) != 1 || mbps == 0 || mbps > 1000) | |
| { | |
| printf("ERROR: Invalid bitrate; exiting ...\n"); | |
| return 1; | |
| } | |
| printf("\n"); | |
| printf("Waiting for metrics ...\r"); | |
| bytes_per_sec = mbps * 125000; | |
| WSADATA wsaData; | |
| if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) | |
| { | |
| printf("ERROR: WSAStartup failed; exiting ...\n"); | |
| return 1; | |
| } | |
| struct addrinfo * res = NULL, hints = { | |
| .ai_socktype = SOCK_DGRAM, | |
| .ai_protocol = IPPROTO_UDP, | |
| }; | |
| int gai = getaddrinfo(TARGET_HOSTNAME, NULL, &hints, &res); | |
| if (gai != 0 || !res) | |
| { | |
| printf("ERROR: getaddrinfo() failed: %d\n", WSAGetLastError()); | |
| WSACleanup(); | |
| return 1; | |
| } | |
| if (res->ai_family == AF_INET) | |
| { | |
| ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(TARGET_PORT); | |
| } | |
| else if (res->ai_family == AF_INET6) | |
| { | |
| ((struct sockaddr_in6 *) res->ai_addr)->sin6_port = htons(TARGET_PORT); | |
| } | |
| SOCKET sock = socket(res->ai_family, SOCK_DGRAM, IPPROTO_UDP); | |
| if (sock == INVALID_SOCKET) | |
| { | |
| printf("ERROR: socket() failed; exiting ...\n"); | |
| WSACleanup(); | |
| return 1; | |
| } | |
| GUID guid = WSAID_TRANSMITPACKETS; | |
| LPFN_TRANSMITPACKETS TransmitPackets = NULL; | |
| DWORD bytes; | |
| WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &TransmitPackets, sizeof(TransmitPackets), &bytes, NULL, NULL); | |
| if (!TransmitPackets) | |
| { | |
| printf("ERROR: TransmitPackets() not available; exiting ...\n"); | |
| closesocket(sock); | |
| WSACleanup(); | |
| return 1; | |
| } | |
| int sndbuf = 32 * 1024 * 1024; | |
| setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf, sizeof(sndbuf)); | |
| BOOL dontFragment = FALSE; | |
| if (res->ai_family == AF_INET) | |
| { | |
| setsockopt(sock, IPPROTO_IP, IP_DONTFRAGMENT, (void *) &dontFragment, sizeof(dontFragment)); | |
| } | |
| else if (res->ai_family == AF_INET6) | |
| { | |
| setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, (void *) &dontFragment, sizeof(dontFragment)); | |
| } | |
| if (connect(sock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) | |
| { | |
| printf("ERROR: connect() failed: %d\n", WSAGetLastError()); | |
| closesocket(sock); | |
| WSACleanup(); | |
| return 1; | |
| } | |
| freeaddrinfo(res); | |
| char buffer[PAYLOAD_SIZE]; | |
| memset(buffer, 0xAB, PAYLOAD_SIZE); | |
| TRANSMIT_PACKETS_ELEMENT elements[BATCH_SIZE]; | |
| for (DWORD i = 0; i < BATCH_SIZE; ++i) | |
| { | |
| elements[i].dwElFlags = TP_ELEMENT_MEMORY | TP_ELEMENT_EOP; | |
| elements[i].cLength = PAYLOAD_SIZE; | |
| elements[i].pBuffer = buffer; | |
| } | |
| QueryPerformanceFrequency(&freq); | |
| void * params[3] = { (void *) sock, (void *) TransmitPackets, (void *) elements }; | |
| HANDLE sender = CreateThread(NULL, 0, sender_thread, params, 0, NULL); | |
| HANDLE reporter = CreateThread(NULL, 0, reporter_thread, NULL, 0, NULL); | |
| WaitForSingleObject(sender, INFINITE); | |
| WaitForSingleObject(reporter, INFINITE); | |
| closesocket(sock); | |
| WSACleanup(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment