How do I close tcp v4 and tcp v6 connections on Windows? I don't want to kill the entire process that has the open connection as this obviously will kick everyone else off that process. I need to do this from a separate process, and so will not have access to socket handles, etc. I am using Windows API to get tcp table, etc. so I know which connections are active.
UPDATE 2022-05-01, found this gem at https://www.x86matthew.com/view_post?id=settcpentry6
UPDATE 2024-04-21, I got the C# PInvoke code working for ipv4 and ipv6. Getting everything in the right byte order was key.
Full .NET 8 code, tested through Windows Vista to 11:
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
// add 2.0.0 version as nuget package
using DigitalRuby.IPBanCore;
namespace YourNamespace;
/// <summary>
/// Socket closer interface
/// </summary>
public interface ISocketCloser
{
/// <summary>
/// Close a socket using low level windows API. Handles ipv4 and ipv6.
/// </summary>
/// <param name="local">Local end point</param>
/// <param name="remote">Remote end point</param>
/// <returns>True if closed, false if not</returns>
bool CloseSocket(IPEndPoint local, IPEndPoint remote);
}
/// <summary>
/// Close sockets on Windows or Linux
/// </summary>
public partial class SocketCloser : ISocketCloser
{
private const int MIB_TCP_STATE_DELETE_TCB = 12;
private static readonly byte[] moduleId = [0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x4A, 0x00, 0xEB, 0x1A, 0x9B, 0xD4, 0x11, 0x91, 0x23, 0x00, 0x50, 0x04, 0x77, 0x59, 0xBC];
private static readonly IntPtr moduleIdPtr;
[LibraryImport("iphlpapi.dll", SetLastError = true)]
private static partial uint SetTcpEntry(ref MIB_TCPROW pTcpRow);
[LibraryImport("nsi.dll", SetLastError = true)]
private static partial uint NsiSetAllParameters(uint action, uint flags, IntPtr moduleId, uint operation, IntPtr buffer, uint bufferLength, IntPtr metric, uint metricLength);
[StructLayout(LayoutKind.Sequential)]
private struct MIB_TCPROW
{
public uint dwState;
public uint dwLocalAddr;
public uint dwLocalPort;
public uint dwRemoteAddr;
public uint dwRemotePort;
}
[StructLayout(LayoutKind.Sequential)]
private struct KillTcpSocketData_V6
{
public ushort wLocalAddressFamily;
public ushort wLocalPort;
public uint bReserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] bLocal;
public uint dwLocalScopeID;
public ushort wRemoteAddressFamily;
public ushort wRemotePort;
public uint bReserved2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] bRemote;
public uint dwRemoteScopeID;
};
static SocketCloser()
{
moduleIdPtr = Marshal.AllocHGlobal(moduleId.Length);
Marshal.Copy(moduleId, 0, moduleIdPtr, moduleId.Length);
}
/// <inheritdoc />
public bool CloseSocket(IPEndPoint local, IPEndPoint remote)
{
if (OSUtility.IsLinux)
{
return CloseSocketLinux(local, remote);
}
else if (OSUtility.IsWindows)
{
return CloseSocketWindows(local, remote);
}
return false;
}
private static bool CloseSocketLinux(IPEndPoint local, IPEndPoint remote)
{
// sudo ss --kill state all src IP_ADDRESS:PORT dst IP_ADDRESS:PORT
string command = $"ss --kill state all src \"{local.Address}:{local.Port}\" dst \"{remote.Address}:{remote.Port}\"";
OSUtility.StartProcessAndWait("sudo", command, 0);
return true;
}
private static bool CloseSocketWindows(IPEndPoint local, IPEndPoint remote)
{
var localPortFixed = (ushort)IPAddress.HostToNetworkOrder((short)local.Port);
var remotePortFixed = (ushort)IPAddress.HostToNetworkOrder((short)remote.Port);
if (local.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
MIB_TCPROW row = new()
{
dwState = MIB_TCP_STATE_DELETE_TCB,
dwLocalAddr = local.Address.ToUInt32(false),
dwLocalPort = (uint)localPortFixed,
dwRemoteAddr = remote.Address.ToUInt32(false),
dwRemotePort = (uint)remotePortFixed
};
var result = SetTcpEntry(ref row);
return result == 0 || result == 317;
}
else if (local.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
{
KillTcpSocketData_V6 row6 = new()
{
wLocalAddressFamily = (ushort)AddressFamily.InterNetworkV6,
wLocalPort = localPortFixed,
bLocal = local.Address.GetAddressBytes(),
bRemote = remote.Address.GetAddressBytes(),
bReserved1 = 0,
bReserved2 = 0,
dwLocalScopeID = (uint)IPAddress.HostToNetworkOrder(local.Address.ScopeId),
dwRemoteScopeID = (uint)IPAddress.HostToNetworkOrder(remote.Address.ScopeId),
wRemoteAddressFamily = (ushort)AddressFamily.InterNetworkV6,
wRemotePort = remotePortFixed
};
// Assume global module ID and other parameters are correctly set
var ptrSize = Marshal.SizeOf<KillTcpSocketData_V6>();
var ptr = Marshal.AllocHGlobal(ptrSize);
try
{
// Call the undocumented API (the values for module ID, etc., must be correct)
Marshal.StructureToPtr(row6, ptr, false);
var result = NsiSetAllParameters(1, 2, moduleIdPtr, 16, ptr, (uint)ptrSize, IntPtr.Zero, 0);
return result == 0 || result == 317;
}
finally
{
// Cleanup
Marshal.FreeHGlobal(ptr);
}
}
return false;
}
}