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:00MACAddress("12:34:56:78:9a:bc") -> 12:34:56:78:9a:bcMACAddress(b"\x12\x34\x56\x78\x9a\xbc") -> 12:34:56:78:9a:bcMACAddress(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