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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
bin/
build/
projects/*
!projects/*.sln
!projects/*.vcxproj
!projects/*.vcxproj.filters
tmp/
zig-cache/
zig-out/
Expand All @@ -8,3 +12,4 @@ etc/clumsy.aps
obj_vs
obj_ninja
obj_gmake
tools/genie.exe
68 changes: 57 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,72 @@
# clumsy
# clumsy 4.0

__clumsy makes your network condition on Windows significantly worse, but in a managed and interactive manner.__

Leveraging the awesome [WinDivert](http://reqrypt.org/windivert.html), clumsy stops living network packets and capture them, lag/drop/tamper/.. the packets on demand, then send them away. Whether you want to track down weird bugs related to broken network, or evaluate your application on poor connections, clumsy will come in handy:
clumsy uses [WinDivert](https://reqrypt.org/windivert.html) to capture live packets, apply impairments such as lag, drop, throttle, duplicate, tamper, reset, and bandwidth limits, then reinject the packets. It is useful for testing applications under poor network conditions without changing application code or setting up a proxy.

* No installation.
* No need for proxy setup or code change in your application.
* System wide network capturing means it works on any application.
* Works even if you're offline (ie, connecting from localhost to localhost).
* Your application keeps running, while clumsy can start and stop anytime.
* Interactive control how bad the network can be, with enough visual feedback to tell you what's going on.
Version 4.0 adds native application-level filtering. You can now limit clumsy's impairments to traffic owned by a selected executable while unrelated traffic passes through normally.

See [this page](http://jagt.github.io/clumsy) for more info and build instructions.
## Download

- [Download clumsy 4.0 for Windows x64](https://github.com/foodak/clumsy/releases/download/v4.0/clumsy-4.0-win64.zip)
- [View all releases](https://github.com/foodak/clumsy/releases)

clumsy must be run as Administrator because it uses WinDivert to intercept packets.

## What's New In 4.0

- Native "Limit to application" controls in the Filtering panel.
- Process-name matching, for example `Game.exe` or `Speedtest.exe`.
- Full-path matching with a Browse button for exact executable selection.
- Multiple targets separated with commas or semicolons.
- Child-process matching for launchers and portable apps, such as `SpeedtestPortable.exe` launching `Speedtest.exe`.
- WinDivert FLOW tracking plus IP Helper seeding so existing and newly opened TCP/UDP flows can be attributed to process IDs.
- Target-aware packet capture that narrows the NETWORK-layer filter to known target TCP/UDP ports when the app filter is enabled.
- Live status counters for target PIDs, target flows, UDP endpoints, affected packets, passed packets, and unknown packets.

## Application Filter Preview

![clumsy 4.0 application filter](clumsy-4.0-application-filter.jpg)

## Quick Start

1. Run clumsy as Administrator.
2. Choose a packet filter, for example `outbound and (tcp or udp)`.
3. Enable the impairment modules you want, such as lag, drop, throttle, or bandwidth.
4. Optional: check "Limit to application".
5. Enter an executable name such as `Speedtest.exe`, or use Browse to select a full path.
6. Click Start.

When the application filter is disabled, clumsy keeps its original system-wide behavior.

## Application Filter

The application filter is built for the common case where you want to test one app without disrupting debuggers, calls, browsers, or other unrelated network activity.

The normal packet filter still controls which traffic clumsy is allowed to capture. When "Limit to application" is enabled, clumsy only applies impairments to TCP/UDP packets attributed to the selected executable name or full path. Non-target and unknown packets are passed through unchanged.

Example:

- Packet filter: `outbound and (tcp or udp)`
- Application: `Speedtest.exe`

Notes:

- Process-name matching is case-insensitive.
- In process-name mode, `game` also matches `game.exe`.
- Browse fills in a full executable path and switches matching to full-path mode.
- Multiple applications can be separated with commas or semicolons.
- Child-process matching is enabled, which helps launchers and portable apps.
- The target app does not need to be running before clumsy starts.
- Unknown or unattributed packets pass through unchanged by design.
- Loopback attribution has the same caveats as clumsy's existing loopback support.

## Details

Simulate network latency, delay, packet loss with clumsy on Windows 7/8/10:
clumsy supports latency, delay, packet loss, throttling, packet duplication, out-of-order delivery, tampering, connection reset, and bandwidth limiting on Windows.

![](clumsy-demo.gif)


## License

MIT
34 changes: 34 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# clumsy 4.0

clumsy 4.0 adds native application-level filtering for Windows.

## Highlights

- New "Limit to application" controls in the Filtering panel.
- Match target apps by process name, such as `Speedtest.exe`, or by full executable path.
- Browse button for selecting an executable and switching to full-path mode.
- Multiple application targets separated with commas or semicolons.
- Child-process matching for launchers and portable apps.
- WinDivert FLOW tracking plus IP Helper seeding for existing and new TCP/UDP flows.
- Target-aware packet capture so broad packet filters do not unnecessarily route unrelated apps through the impairment loop.
- Live status counters for target PIDs, target flows, UDP endpoints, affected packets, passed packets, and unknown packets.

## Download

Download `clumsy-4.0-win64.zip`, extract it, and run `clumsy.exe` as Administrator.

The zip contains:

- `clumsy.exe`
- `WinDivert.dll`
- `WinDivert64.sys`
- `iup.dll`
- `config.txt`
- `LICENSE.txt`
- `README.md`

## Notes

- Unknown or unattributed packets pass through unchanged.
- The application filter does not use `processId` in a NETWORK-layer WinDivert filter.
- Portable launchers can be targeted directly when child-process matching finds the real network-owning child process.
5 changes: 3 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub fn build(b: *std.build.Builder) void {

exe.step.dependOn(&cmd.step);
exe.addObjectFile(res_obj_path);
exe.addCSourceFile("src/app_filter.c", &.{""});
exe.addCSourceFile("src/bandwidth.c", &.{""});
exe.addCSourceFile("src/divert.c", &.{""});
exe.addCSourceFile("src/drop.c", &.{""});
Expand All @@ -109,7 +110,6 @@ pub fn build(b: *std.build.Builder) void {
exe.addCSourceFile("src/tamper.c", &.{""});
exe.addCSourceFile("src/throttle.c", &.{""});
exe.addCSourceFile("src/utils.c", &.{""});
exe.addCSourceFile("src/utils.c", &.{""});

if (arch == .x86)
exe.addCSourceFile("etc/chkstk.s", &.{""});
Expand All @@ -130,6 +130,7 @@ pub fn build(b: *std.build.Builder) void {
exe.linkSystemLibrary("comctl32");
exe.linkSystemLibrary("Winmm");
exe.linkSystemLibrary("ws2_32");
exe.linkSystemLibrary("iphlpapi");
exe.linkSystemLibrary("kernel32");
exe.linkSystemLibrary("gdi32");
exe.linkSystemLibrary("comdlg32");
Expand Down Expand Up @@ -174,4 +175,4 @@ pub const RemoveOutFile = struct {
const out_dir = try std.fs.openDirAbsolute(self.builder.exe_dir, .{});
try out_dir.deleteFile(self.rel_path);
}
};
};
Binary file added clumsy-4.0-application-filter.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion etc/clumsy.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- the funky thing is that the version MUST be x.x.x.x, see msdn for info -->
<assemblyIdentity
version="0.3.0.0"
version="4.0.0.0"
processorArchitecture="x86"
name="clumsy"
type="win32"
Expand Down
2 changes: 1 addition & 1 deletion etc/clumsy64.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- the funky thing is that the version MUST be x.x.x.x, see msdn for info -->
<assemblyIdentity
version="0.3.0.0"
version="4.0.0.0"
processorArchitecture="amd64"
name="clumsy"
type="win32"
Expand Down
5 changes: 4 additions & 1 deletion etc/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# filter-name:filter-text
# see <https://github.com/basil00/Divert/wiki/WinDivert-Documentation#7-filter-language> for details

# Default: capture normal IPv4 and IPv6 traffic in both directions.
ipv4 + ipv6 all: ip or ipv6

# loopback packets can only be filtered using 'outbound'.
localhost ipv4 all: outbound and loopback
localhost ipv4 tcp: tcp and outbound and loopback
Expand All @@ -24,4 +27,4 @@ udp ipv4 against port: udp and (udp.DstPort == 12354 or udp.SrcPort == 12354)
ipv6 all: ipv6

# you can add your usual filters here for your own use:
#http requests ONLY(data transmit on other ports): outbound and tcp.DstPort == 80
#http requests ONLY(data transmit on other ports): outbound and tcp.DstPort == 80
33 changes: 21 additions & 12 deletions genie.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
-- genie, https://github.com/bkaradzic/GENie
-- known working version
-- known working version (for VS2019 and earlier):
-- https://github.com/bkaradzic/bx/blob/51f25ba638b9cb35eb2ac078f842a4bed0746d56/tools/bin/windows/genie.exe
--
-- For VS2022 support, use a recent GENie built from master:
-- https://github.com/bkaradzic/GENie
-- Pre-built genie.exe can also be found in the bx repo tools/bin/windows/.
--
-- Supported actions: vs2022, vs2019, vs2017, vs2015, gmake
-- Usage: genie.exe vs2022

MINGW_ACTION = 'gmake'

if _ACTION == 'clean' then
os.rmdir('./build')
os.rmdir('./projects')
os.rmdir('./bin')
os.rmdir('./obj_vs')
os.rmdir('./obj_' .. MINGW_ACTION)
Expand All @@ -30,14 +37,14 @@ local ROOT = os.getcwd()
print(ROOT)

solution('clumsy')
location("./build")
location("./projects")
configurations({'Debug', 'Release'})
platforms({'x32', 'x64'})

project('clumsy')
language("C")
files({'src/**.c', 'src/**.h'})
links({'WinDivert', 'iup', 'comctl32', 'Winmm', 'ws2_32'})
links({'WinDivert', 'iup', 'comctl32', 'Winmm', 'ws2_32', 'Iphlpapi'})
if string.match(_ACTION, '^vs') then -- only vs can include rc file in solution
files({'./etc/clumsy.rc'})
elseif _ACTION == MINGW_ACTION then
Expand All @@ -56,7 +63,7 @@ solution('clumsy')
kind("WindowedApp")

configuration(MINGW_ACTION)
links({'kernel32', 'gdi32', 'comdlg32', 'uuid', 'ole32'}) -- additional libs
links({'kernel32', 'gdi32', 'comdlg32', 'uuid', 'ole32', 'iphlpapi'}) -- additional libs
buildoptions({
'-Wno-missing-braces',
'-Wno-missing-field-initializers',
Expand All @@ -65,19 +72,18 @@ solution('clumsy')
objdir('obj_'..MINGW_ACTION)

configuration("vs*")
defines({"_CRT_SECURE_NO_WARNINGS"})
defines({"_CRT_SECURE_NO_WARNINGS", "_CRT_NONSTDC_NO_DEPRECATE"})
flags({'NoManifest'})
kind("WindowedApp") -- We don't need the console window in VS as we use OutputDebugString().
buildoptions({'/wd"4214"'})
linkoptions({'/ENTRY:"mainCRTStartup" /SAFESEH:NO'})
-- characterset("MBCS")
-- MBCS is the default in GENie when 'Unicode' flag is not set
includedirs({LIB_DIVERT_VC11 .. '/include'})
objdir('obj_vs')

configuration({'x32', 'vs*'})
-- defines would be passed to resource compiler for whatever reason
-- and ONLY can be put here not under 'configuration('x32')' or it won't work
defines({'X32'})
linkoptions({'/ENTRY:"mainCRTStartup"', '/SAFESEH:NO'})
includedirs({LIB_IUP_WIN32_VC11 .. '/include'})
libdirs({
LIB_DIVERT_VC11 .. '/x86',
Expand All @@ -86,6 +92,7 @@ solution('clumsy')

configuration({'x64', 'vs*'})
defines({'X64'})
linkoptions({'/ENTRY:"mainCRTStartup"'})
includedirs({LIB_IUP_WIN64_VC11 .. '/include'})
libdirs({
LIB_DIVERT_VC11 .. '/x64',
Expand Down Expand Up @@ -141,10 +148,12 @@ solution('clumsy')
targetdir(subdir)
debugdir(subdir)
if platform == 'vs*' then
-- robocopy returns 1 on success, which MSBuild treats as error.
-- All commands run in one batch; final exit /B 0 forces success.
postbuildcommands({
"robocopy " .. divert_lib .." " .. subdir .. ' *.dll *.sys >> robolog.txt',
"robocopy " .. iup_lib .. " " .. subdir .. ' iup.dll >> robolog.txt',
"robocopy " .. ROOT .. "/etc/ " .. subdir .. ' config.txt >> robolog.txt',
"robocopy " .. divert_lib .." " .. subdir .. ' *.dll *.sys > NUL',
"robocopy " .. iup_lib .. " " .. subdir .. ' iup.dll > NUL',
"robocopy " .. ROOT .. "/etc/ " .. subdir .. ' config.txt > NUL',
"exit /B 0"
})
elseif platform == MINGW_ACTION then
Expand Down
29 changes: 29 additions & 0 deletions projects/clumsy.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2022
# Generated by GENie https://github.com/bkaradzic/GENie
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "clumsy", "clumsy.vcxproj", "{C218B2F6-AEBA-DCCC-9775-F02083B6631D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Debug|Win32.ActiveCfg = Debug|Win32
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Debug|Win32.Build.0 = Debug|Win32
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Debug|x64.ActiveCfg = Debug|x64
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Debug|x64.Build.0 = Debug|x64
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Release|Win32.ActiveCfg = Release|Win32
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Release|Win32.Build.0 = Release|Win32
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Release|x64.ActiveCfg = Release|x64
{C218B2F6-AEBA-DCCC-9775-F02083B6631D}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
EndGlobal
Loading