# IPv6 Support Implementation in Shadowsocks‑Windows: Architecture and Differences from IPv4

> Explore Shadowsocks-Windows IPv6 support implementation. Discover how the global isIPv6Enabled flag impacts socket creation, binding, and SOCKS5 protocol, improving network security.

- Repository: [shadowsocks/shadowsocks-windows](https://github.com/shadowsocks/shadowsocks-windows)
- Tags: architecture
- Published: 2026-03-05

---

**Shadowsocks‑Windows implements IPv6 support through a global `isIPv6Enabled` configuration flag that cascades through socket creation, endpoint binding, and SOCKS5 protocol handling to ensure all network components use the correct address family.**

The IPv6 support implementation in shadowsocks/shadowsocks-windows is gated by an experimental feature flag that forces the entire application stack to adopt IPv6 addressing when enabled. Unlike IPv4, which uses 32-bit addresses and `AddressFamily.InterNetwork`, the IPv6 pathway requires 16-byte address handling, bracketed URI notation, and specific SOCKS5 address type identifiers. This architecture ensures that when `isIPv6Enabled` is true, every socket, listener, and relay operates exclusively on the IPv6 loopback and any-address interfaces.

## Configuration and Feature Flag

The foundation of IPv6 support resides in [`Model/Configuration.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Model/Configuration.cs), where the Boolean property `isIPv6Enabled` determines the address family for the entire application. At startup, the code validates this setting against `Socket.OSSupportsIPv6` (lines 46-215); if the operating system lacks IPv6 capability, the flag is forced to `false` regardless of user preference.

When IPv6 is active, the `LocalHost` property (line 126) returns `"[::1]"` instead of `"127.0.0.1"`, ensuring that loopback connections use the correct IPv6 loopback interface:

```csharp
// Model/Configuration.cs
public string LocalHost => isIPv6Enabled ? "[::1]" : "127.0.0.1";

```

This single configuration value propagates to every network component, acting as the source of truth for address family selection throughout the codebase.

## Socket Creation and Listener Binding

In [`Controller/Service/Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Controller/Service/Listener.cs), the application creates TCP and UDP sockets based on the configuration flag. Lines 70-77 instantiate sockets using `AddressFamily.InterNetworkV6` when IPv6 is enabled, falling back to `AddressFamily.InterNetwork` for IPv4-only operation:

```csharp
// Controller/Service/Listener.cs
_socket = new Socket(
    config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork,
    SocketType.Stream,
    ProtocolType.Tcp);

```

Endpoint binding follows the same conditional logic. Lines 35-39 select between `IPAddress.IPv6Any` and `IPAddress.Any` (or their loopback equivalents) to ensure the listener attaches to the correct network interface:

```csharp
// Controller/Service/Listener.cs
var localAddress = config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any;

```

## SOCKS5 Protocol Handling

The TCP relay layer in [`Controller/Service/TCPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Controller/Service/TCPRelay.cs) implements IPv6-specific parsing for SOCKS5 protocol packets. [`Encryption/EncryptorBase.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Encryption/EncryptorBase.cs) defines the address type constant `ATYP_IPv6 = 0x04` (line 72), which the relay uses to identify 16-byte IPv6 payloads in request and response handling (lines 481, 540, and 579).

When parsing incoming SOCKS5 requests, the relay extracts IPv6 addresses using a dedicated case block that handles the 16-byte address length:

```csharp
// Controller/Service/TCPRelay.cs
case ATYP_IPv6: // IPv6 address, 16 bytes
    Buffer.BlockCopy(data, offset, response, 4, 16);
    offset += 16;
    break;

```

This differs from IPv4, which uses `ATYP_IPv4 = 0x01` and copies only 4 bytes.

## Server URI Formatting

IPv6 addresses require bracket notation to comply with RFC 3986. In [`Model/Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Model/Server.cs) (line 129), the code detects IPv6 hostnames and wraps them in square brackets when constructing connection strings:

```csharp
// Model/Server.cs
case UriHostNameType.IPv6:
    // Add square brackets when IPv6 (RFC3986)
    return $"[{host}]";

```

This produces valid URIs such as `http://[2001:db8::1]:8388`, whereas IPv4 addresses are appended directly without delimiters.

## UDP Relay and Privoxy Integration

The UDP relay implementation in [`Controller/Service/UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Controller/Service/UDPRelay.cs) mirrors the TCP listener’s behavior. At line 71, it returns `IPAddress.IPv6Any` when the global IPv6 flag is enabled, ensuring UDP sockets bind to the correct any-address interface.

The bundled Privoxy proxy also respects this setting. [`Controller/Service/PrivoxyRunner.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Controller/Service/PrivoxyRunner.cs) (lines 55-58 and 150-156) passes the `isIPv6Enabled` flag to `GetFreePort()`, which binds the helper HTTP proxy to an IPv6 loopback address when necessary. This guarantees that the local proxy chain remains consistent with the chosen address family.

## Key Differences from IPv4

The IPv6 support implementation differs from IPv4 in five critical ways:

- **Address Family Selection** – All sockets use `InterNetworkV6` instead of `InterNetwork`, affecting every connection in the stack.
- **Address Length** – SOCKS5 packets carry 16-byte IPv6 addresses (type `0x04`) versus 4-byte IPv4 addresses (type `0x01`).
- **Loopback Interfaces** – The code uses `[::1]` and `IPv6Loopback` instead of `127.0.0.1` and `Loopback`.
- **URI Syntax** – IPv6 server addresses are wrapped in `[...]` brackets to conform to RFC 3986.
- **OS Validation** – The application automatically disables IPv6 if `Socket.OSSupportsIPv6` returns false, preventing runtime errors on IPv4-only systems.

## Summary

- The `isIPv6Enabled` flag in [`Configuration.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Configuration.cs) controls the entire application's address family, defaulting to `false` for backward compatibility.
- Socket creation in [`Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Listener.cs) switches between `AddressFamily.InterNetworkV6` and `InterNetwork` based on the configuration flag.
- SOCKS5 protocol handling requires a separate code path for the `ATYP_IPv6` address type to process 16-byte addresses.
- IPv6 addresses in URIs must be bracketed, implemented in [`Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Server.cs) for RFC 3986 compliance.
- Both TCP and UDP relays, along with the Privoxy helper, bind to `IPv6Any` or `IPv6Loopback` when IPv6 mode is active.

## Frequently Asked Questions

### How do I enable IPv6 support in Shadowsocks‑Windows?

Set `"isIPv6Enabled": true` in your configuration file or enable the option in the GUI settings. The application will verify that your operating system supports IPv6 via `Socket.OSSupportsIPv6` before activating the feature. If the OS check fails, the flag is automatically forced to `false` to prevent connection errors.

### Why does Shadowsocks‑Windows use different address types for IPv4 and IPv6 in SOCKS5 packets?

The SOCKS5 protocol defines specific address type identifiers: `0x01` for IPv4 (4 bytes) and `0x04` for IPv6 (16 bytes). The [`TCPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/TCPRelay.cs) implementation checks these type bytes to determine how many bytes to copy from the packet buffer—4 bytes for IPv4 or 16 bytes for IPv6—ensuring correct address parsing regardless of the protocol version.

### What happens to the local listener when IPv6 is enabled?

When `isIPv6Enabled` is true, [`Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Listener.cs) creates sockets with `AddressFamily.InterNetworkV6` and binds them to `IPAddress.IPv6Any` or `IPAddress.IPv6Loopback` instead of their IPv4 equivalents. This restricts all local proxy traffic to the IPv6 loopback interface `[::1]`, isolating the listener from IPv4-only network stacks.

### Does the UDP relay support IPv6 addressing?

Yes. [`UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UDPRelay.cs) handles IPv6 by returning `IPAddress.IPv6Any` when the global IPv6 flag is enabled (line 71). This ensures that UDP socket creation and binding follow the same address family selection logic as the TCP components, maintaining protocol consistency across the application.