Module: ldn

Implements the local wireless protocol used by the Nintendo Switch.

class MACAddress
Class that represents a MAC address.

class NetworkInfo
Holds information about a LDN network.

class ParticipantInfo
Holds information about a network participant.

class ConnectNetworkParam
Contains various parameters for joining a network.

class CreateNetworkParam
Contains various parameters for network creation.

class STANetwork
Represents an active LDN network for a station.

class APNetwork
Represents an active LDN network for an access point.

async def scan(keys: dict[str, bytes], ifname: str = "ldn", phyname: str = "phy0", channels: list[int] = [1, 6, 11], dwell_time: float=.110, protocols: list[int] = [1, 3]) -> list[NetworkInfo]
Searches for nearby LDN networks on the given WLAN channels. To perform the scanning, this function creates a new interface on the given wiphy. The given interface name must not already be in use. The protocol should be 1 (NX) or 3.

The keys can be read from a prod.keys file with the load_keys function.

async with connect(param: ConnectNetworkParam) -> STANetwork
Joins an active LDN network. The station is disconnected automatically at the end of the async with block.

async with create_network(param: CreateNetworkParam) -> APNetwork
Creates a new LDN network. The network is destroyed automatically at the end of the async with block.

def load_keys(path: str) -> dict[str, bytes]
Loads encryption keys from a prod.keys file. These can be dumped from a Switch device.

Global Constants

ACCEPT_ALL = 0
ACCEPT_NONE = 1
ACCEPT_BLACKLIST = 2
ACCEPT_WHITELIST = 3

PLATFORM_NX = 0
PLATFORM_OUNCE = 1

MACAddress

def __init__(address: str | bytes | int = None)
Creates a new MAC address. If an address is given, the MAC address is parsed from the given string, bytes or integer object. Examples:
MACAddress() -> 00:00:00:00:00:00
MACAddress("12:34:56:78:9a:bc") -> 12:34:56:78:9a:bc
MACAddress(b"\x12\x34\x56\x78\x9a\xbc") -> 12:34:56:78:9a:bc
MACAddress(0x123456789abc) -> 12:34:56:78:9a:bc

def __eq__(other: MACAddress) -> bool
Checks if two MAC addresses are equal.

def __hash__() -> int
Returns a hash so that the MAC address can be used in sets and as dictionary keys.

def __bytes__() -> bytes
Returns a bytes representation of the MAC address: \x12\x34\x56\x78\x9a\xbc.

def __str__() -> str
Returns a string representation of the MAC address: 12:34:56:78:9a:bc.

def __repr__() -> str
Returns a different string representation of the MAC address: MACAddress('12:34:56:78:9a:bc').

NetworkInfo

local_communication_id: int
This is usually the title id of the game.
app_version: int
The application communication version of the host.
scene_id: int
Scene id (defined by game).

accept_policy: int
Defines which stations are accepted by the host. One of the ACCEPT_ constants.
max_participants: int
Maximum number of participants (up to 8).
num_participants: int
Current number of connected participants.
participants: list[ParticipantInfo]
Current network participants. This list always contains exactly 8 entries.
application_data: bytes
Additional information provided by game.

address: MACAddress
The MAC address of the network host.
band: int
The WLAN band of the network (2 or 5).
channel: int
The WLAN channel of the network.
ssid: bytes
The SSID of the network (16 random bytes).

version: int
LDN version used by the host.
server_random: bytes
Random bytes generated by the host. These are used to generate encryption keys.
security_level: int
The security level of the network.

ParticipantInfo

connected: bool
Indicates whether this entry is valid.
ip_address: str
The IP address of the participant (169.254.X.Y).
mac_address: MACAddress
The MAC address of the participant.
name: bytes = b""
The nickname of the participant.
app_version: int = 0
The application communication version of the participant.
platform: int
The platform that the participant is playing on. One of the PLATFORM_ constants.

ConnectNetworkParam

def __init__()
Creates a new instance with the default values. The network and keys fields are always required.

ifname: str = "ldn"
The interface name for the station. The interface names must not already be in use.
phyname: str = "phy0"
The name of the wiphy on which the station interface is created.

network: NetworkInfo
The network information obtained during scanning.
password: bytes = b""
Password/passphrase. This is used to generate encryption keys. Authentication fails if the password is wrong.

name: bytes
Your nickname (up to 32 bytes)
app_version: int
Your application communication version.
platform: int = PLATFORM_NX
The platform that you are playing on. Must be one of the PLATFORM_ constants.

enable_challenge: bool = True
Specifies whether the DRM challenge is enabled. This is always enabled for games, but not for system titles.
device_id: int = random.randint(0, 0xFFFFFFFFFFFFFFFF)
The device id for the DRM challenge.

client_random: bytes | None = None
Must be 16 bytes. This is used during authentication. If None, a random value is generated automatically.

keys: dict[str, bytes]
The keys that are loaded from prod.keys. These can be loaded from a file with the load_keys function.
dev: bool = False
If enabled, a different key is used for the local concurrency check.

override_advertise_key: bytes | None = None
Overrides the key that is used to decrypt the advertisement frames. This is normally not required, but can be useful while researching new protocol versions.
override_data_key: bytes | None = None
Overrides the key that is used to encrypt data frames. This is normally not required, but can be useful while researching new protocol versions.
override_challenge_key: bytes | None = None
Overrides the HMAC key that is used for the local concurrency check. This is normally not required.

CreateNetworkParam

def __init__()
Creates a new instance with the default values. The local_communication_id, scene_id, name, app_version and keys fields are always required.

ifname: str = "ldn"
The interface name for the access point. The interface names must not already be in use.
ifname_monitor: str = "ldn-mon"
The interface name for the monitor. The interface names must not already be in use.
ifname_tap: str = "ldn-tap"
A name for the TAP interface. The interface names must not already be in use.
phyname: str = "phy0"
The name of the wiphy on which the access point interface are created. phyname_monitor: str = "phy0"
The name of the wiphy on which the monitor interface is created.

local_communication_id: int
This is usually the title id.
scene_id: int
The game mode.

max_participants: int = 8
The maximum number of participants. Cannot be higher than 8.
application_data: bytes = b""
Game-specific data. Can be updated at any time after network creation.
accept_policy: int = ACCEPT_ALL
Specifies which stations are allowed to join the network. Must be one of the ACCEPT_ constants.
accept_filter: list[MACAddress] = []
This list contains a blacklist or whitelist, depending on the accept policy.
security_level: int = 1
The security level of the network. Always 1 in practice.
ssid: bytes | None = None
Must contain exactly 16 bytes. If None, a random SSID is generated during network creation.

name: bytes
Your nickname (up to 32 bytes)
app_version: int
Your application communication version.
platform: int = PLATFORM_NX
The platform that you are playing on. Must be one of the PLATFORM_ constants.

channel: int | None = None
The WLAN channel of the network. If None, the channel is chosen randomly from 1, 6 or 11 during network creation.
server_random: bytes | None = None
Must be 16 bytes. This is used to generate encryption keys. If None, a random key is generated during network creation.
password: bytes = b""
Password/passphrase. This is used to generate encryption keys.

version: int = 4
LDN version (2, 3 or 4).
enable_challenge: bool = True
Specifies whether the DRM challenge is enabled. This is always enabled for games, but not for system titles.
device_id: int = random.randint(0, 0xFFFFFFFFFFFFFFFF)
The device id for the DRM challenge.

protocol: int = 1
The protocol to use, must be 1 (NX) or 3.

keys: dict[str, bytes]
Encryption keys loaded from prod.keys.
dev: bool = False
If enabled, a different key is used for the local concurrency check.

override_advertise_key: bytes | None = None
Overrides the key that is used to encrypt the advertisement frames. This is normally not required, but can be useful while researching new protocol versions.
override_data_key: bytes | None = None
Overrides the key that is used to encrypt data frames. This is normally not required, but can be useful while researching new protocol versions.
override_challenge_key: bytes | None = None
Overrides the HMAC key that is used for the local concurrency check. This is normally not required.

STANetwork

def info() -> NetworkInfo
Returns information about the network.

def participant() -> ParticipantInfo
Returns information about the local participant.

def broadcast_address() -> str
Returns the broadcast address of the network.

async def next_event() -> object
Waits until an event occurs and returns it. Returns JoinEvent, LeaveEvent, DisconnectEvent, ApplicationDataChanged or AcceptPolicyChanged.

APNetwork

def info() -> NetworkInfo
Returns information about the network.

def participant() -> ParticipantInfo
Returns information about the local participant.

def broadcast_address() -> str
Returns the broadcast address of the network.

def set_application_data(data: bytes) -> None
Updates the application data.

def set_accept_policy(policy: int) -> None
Updates the station accept policy (one of the ACCEPT_ constants).

def set_accept_filter(filter: list[MACAddress]) -> None
Updates the accept filter. This is either a blacklist or whitelist, depending on the accept policy.

async def kick(index: int) -> None
Kicks a station from the network.

async def next_event() -> object
Waits until an event occurs and returns it. Returns either JoinEvent or LeaveEvent.

JoinEvent

Triggered when a new station joins the network.

index: int
participant: ParticipantInfo

LeaveEvent

Triggered when a station leaves the network.

index: int
participant: ParticipantInfo

DisconnectEvent

Triggered when you are disconnected from the network.

reason: int

ApplicationDataChanged

Triggered when the host has changed the application data.

old: bytes
new: bytes

AcceptPolicyChanged

Triggered when the host has changed the station accept policy.

old: int
new: int