LDN services: Difference between revisions

 
(59 intermediate revisions by the same user not shown)
Line 1: Line 1:
LDN handles all local network communication.
LDN handles all local network communication.
There's 2 IPC handler threads for all ldn:* services.


= ldn:m =
= ldn:m =
This is "nn::ldn::detail::IMonitorServiceCreator".
This is "nn::ldn::detail::IMonitorServiceCreator".
This has IPC max_sessions 5.
[20.2.0+] This has max_sessions 6.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 41: Line 47:


=== GetState ===
=== GetState ===
No input, returns an output u32.
No input, returns an output [[#State|u32]].


sdknso implements this by <code>return</code>ing the u32, with 0 being returned on error.
sdknso implements this by <code>return</code>ing the u32, with 0 being returned on error.
Line 84: Line 90:
= ldn:s =
= ldn:s =
This is "nn::ldn::detail::ISystemServiceCreator".
This is "nn::ldn::detail::ISystemServiceCreator".
This has IPC max_sessions 5.
[18.0.0+] The sdknso uses SessionManager with this, where the additional session-count is 0x3.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 97: Line 107:
No input. Returns an [[#ISystemLocalCommunicationService]].
No input. Returns an [[#ISystemLocalCommunicationService]].


The user-process closes the ISystemServiceCreator object immediately after using this cmd. Official sw ignores errors from this cmd.
The user-process closes the ISystemServiceCreator object once finished with it during initialization. Official sw ignores errors from this cmd.


== CreateClientProcessMonitor ==
== CreateClientProcessMonitor ==
Line 197: Line 207:
=== GetNetworkInfo ===
=== GetNetworkInfo ===
Takes a type-0x1A output buffer containing a [[#NetworkInfo]].
Takes a type-0x1A output buffer containing a [[#NetworkInfo]].
[[#GetState|State]] must be 3 or 5.


=== GetIpv4Address ===
=== GetIpv4Address ===
Line 253: Line 265:


=== SetProtocol ===
=== SetProtocol ===
Takes an input u32, no output.
Takes an input [[#Protocol|u32]], no output.


This cmd was implemented with [20.0.0+], prior to that this just returned an error.
This cmd was implemented with [20.0.0+], prior to that this just returned an error.


The sdk user-process func will pass value 1 to the cmd when the input [[#Protocol|Protocol]] is 0/1, 3 is passed directly if specified, otherwise Abort. User-processes use SetProtocol immediately after initializing ldn.
The sdk user-process func will pass value 1 to the cmd when the input [[#Protocol|Protocol]] is 0/1, 3 is passed directly if specified, otherwise Abort. User-processes use SetProtocol immediately after initializing ldn.
[20.0.0+] The ldn initialization functionality in sdknso also uses this with value 1 (NX) eventually after the init cmd was used successfully.


The input is validated, then a vfunc is called.
The input is validated, then a vfunc is called.


The cmd_input must be non-zero. BIT(cmd_input) must be set in a state-field, otherwise a separate Result is returned.
The cmd_input must be non-zero. BIT(cmd_input) must be set in a state-field, otherwise a separate Result is returned. This is a permission [[#Protocol|bitmask]] which originates from the ldn:* service object being used.
 
The above state field originates from a ldn:u/ldn:s state field, which on NX is always set to 0xA. Therefore, on NX the above bit check only allows value 1 or 3.


The vfunc sends a message to another thread with the input u32 as the param, and returns the response from that.
The vfunc sends a message to another thread with the input u32 as the param, and returns the response from that.


The thread msg-queue-handler (besides other validation) uses the input param to select what values to write to state fields. Only input value 1 or 3 is allowed, with an error being thrown otherwise. The previously mentioned validation includes verifying that [[#GetState|State]] is Initialized.
The thread msg-queue-handler (besides other validation) uses the input param to select what values to write to state fields. On NX only input value 1 or 3 is allowed, with an error being thrown otherwise. The previously mentioned validation includes verifying that [[#GetState|State]] is Initialized.


=== OpenAccessPoint ===
=== OpenAccessPoint ===
Line 286: Line 298:
Unlike CreateNetworkPrivate, this overwrites the channel field in the [[#NetworkConfig]]. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is true, the output from [[Settings_services|GetLdnChannel]] will overwrite that field if the s32 setting value is >=0, otherwise the original value is used. Otherwise when the IsDevelopment field is false (retail), the channel is overwritten with value 0.
Unlike CreateNetworkPrivate, this overwrites the channel field in the [[#NetworkConfig]]. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is true, the output from [[Settings_services|GetLdnChannel]] will overwrite that field if the s32 setting value is >=0, otherwise the original value is used. Otherwise when the IsDevelopment field is false (retail), the channel is overwritten with value 0.


This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail) ([?+] [[System_Settings|system-setting]] <code>ldn!enable_static_security_mode_configuration</code> is checked for being true instead), value 1 is used ([?+] value from [[System_Settings|system-setting]] <code>ldn!static_security_mode</code> is used, with fallback to value 1 if the setting is >=0x4), otherwise the original value is used.
This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail) ([18.0.0+] [[System_Settings|system-setting]] <code>ldn!enable_static_security_mode_configuration</code> is checked for being true instead), value 1 is used ([18.0.0+] value from [[System_Settings|system-setting]] <code>ldn!static_security_mode</code> is used, with fallback to value 1 if the setting is >=0x4), otherwise the original value is used.


[[#GetState|State]] must be 2, this cmd eventually sets the State to value 3.
[[#GetState|State]] must be 2, this cmd eventually sets the State to value 3.
Line 350: Line 362:
This is identical to [[#ConnectPrivate]] (besides the below), except the data internally passed for [[#SecurityParameter]]/[[#NetworkConfig]] are loaded from the input [[#NetworkInfo]].
This is identical to [[#ConnectPrivate]] (besides the below), except the data internally passed for [[#SecurityParameter]]/[[#NetworkConfig]] are loaded from the input [[#NetworkInfo]].


[1.0.0-?] This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail), value 1 is used, otherwise the used value is: original_field == 0 ? {u16 [[#NetworkInfo]]+0x60} : original_field. [?+] This now uses the same SecurityMode override as [[#CreateNetwork|CreateNetwork]].
[1.0.0-?] This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail), value 1 is used, otherwise the used value is: original_field == 0 ? {u16 [[#NetworkInfo]]+0x60} : original_field. [18.0.0+] This now uses the same SecurityMode override as [[#CreateNetwork|CreateNetwork]].


u32 LocalCommunicationVersion>>15 must be 0.
u32 LocalCommunicationVersion>>15 must be 0.
Line 359: Line 371:
See [[#Connect]].
See [[#Connect]].


[1.0.0-?] This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail), value 1 is used, otherwise the original value is used. [?+] This now uses the same SecurityMode override as [[#Connect|Connect]].
[1.0.0-?] This overwrites the u16 field at [[#SecurityConfig]]+0. When the cached [[SPL_services#IsDevelopment|IsDevelopment]] value is false (retail), value 1 is used, otherwise the original value is used. [18.0.0+] This now uses the same SecurityMode override as [[#Connect|Connect]].


=== Disconnect ===
=== Disconnect ===
Line 371: Line 383:
This is used immediately after object creation.
This is used immediately after object creation.


With [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]] is used instead. The cmd impl for Initialize uses [[#InitializeWithVersion|InitializeWithVersion]] with input_u32=0.
On old sysvers the cmd impl for User/System are identical, except different params are used for the funcs called internally.
 
With [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]] is used instead. The cmd impl for Initialize uses [[#InitializeWithVersion|InitializeWithVersion]] with version=0.


=== Finalize ===
=== Finalize ===
Line 390: Line 404:


=== InitializeWithVersion ===
=== InitializeWithVersion ===
Takes an input PID, an u32, and an u64 pid_placeholder.
Takes an input PID, a s32 version, and an u64 pid_placeholder.
 
The priority is determined by whether the interface is User/System: System = 0x38, User = 0x5A.
 
It then calls the init func, with the cmd input params and the above priority, returning the Result on failure.


Official sw uses hard-coded value 0x1 for the u32.
On newer sysvers this then adds an entry for the state array used by [[#RegisterClient|RegisterClient]].


The input u32 is ignored, the impl for this cmd is identical to [[#Initialize_2|Initialize]].
Lastly the input PID and version are written into state, then this returns.


Internally this calls a func with params: (..., PID, &{u16 value 0x38}). On success, this then calls another func (which sets two state fields to the input) with params: (state, 0) (these state fields are used during [[#Authentication]] to check which service is being used).
The init func does the following:
* The PID must be non-zero, and the version must not be negative. The priority must be 0x5A or 0x38.
* An error is returned if state fields are invalid.
* The input PID and version are written into state (a state field is also set to interface == User).
* Lastly, a vfunc is called with the input priority, returning the Result from that.


The first func uses various [[Network_Interface_services|nifm]] funcs. The input value is used to determine the value for [[Network_Interface_services#CreateRequest|nn::nifm::RequestParameters]]: essentially, value 0x4 is used for ldn:u, and value 0x8 is used for ldn:s. The input value is also copied into state.
The vfunc does the following:
* On newer sysvers, this uses [[Shared_Database_services|pl:s]] RequestApplicationFunctionAuthorizationByProcessId with the input PID and [[Shared_Database_services|ApplicationFunctionAuthorizationId]] = 2 (SecureLdnLocalCommunication), returning the Result on failure.
* Then a message is sent to a msg-queue with the input priority.


[[#GetState|State]] must be 0, this cmd eventually sets the State to value 1.
The handler for the above message does the following:
* When state is already initialized, runs handling for that. An error is also thrown if the input priority is larger than a state field.
* Initializes state, etc.
* Various [[Network_Interface_services|nifm]] funcs are eventually used. The input priority is used to determine the value for [[Network_Interface_services#CreateRequest|nn::nifm::RequestParameters]]: value 0x4 or value 0x8 is used, depending on priority > 0x59.
** Newer versions also handle ldn lan_emulation [[System_Settings|sys-settings]] here. For the above value, when lan_emulation is enabled it uses value 0x17, with 0x18 additionally used for priority <= 0x59.
* The rest is state init, including setting [[#State|State]] to value 1. Then 0 is returned.
 
On old sysvers the cmd impl for User/System are identical, except different params are used for the funcs called internally. With newer sysvers the cmd impl is now identical.
 
Version values passed by official sw:
 
{| class="wikitable" border="1"
|-
! Value || SystemVersion
|-
| 0x1 || [7.0.0+]
|-
| 0x2 || [18.0.0+]
|-
| 0x3 || [19.0.0+]
|-
| 0x4 || [20.0.0+]
|}


=== InitializeWithPriority ===
=== InitializeWithPriority ===
Takes an input PID, an u32, an u32, and an u64 pid_placeholder.
Takes an input PID, a s32 version, a s32 priority, and an u64 pid_placeholder.


This is similar to [[#InitializeWithVersion|InitializeWithVersion]], with the additional cmd input u32 now being passed directly to the init func which is called here.
This is similar to [[#InitializeWithVersion|InitializeWithVersion]]. The input priority is passed directly to the init func which is called here, instead of determining it from whether the interface is User/System.
 
Official sw passes input value 0x38 for the priority as the default, when the user doesn't specify the priority.


=== EnableActionFrame ===
=== EnableActionFrame ===
Takes an input [[#ActionFrameSettings]]. No output.
Takes an input [[#ActionFrameSettings]]. No output.
[[#State|State]] must be Initialized.


=== DisableActionFrame ===
=== DisableActionFrame ===
No input/output.
No input/output.
[[#State|State]] must be Initialized.


=== SendActionFrame ===
=== SendActionFrame ===
Takes a type-0x21 input buffer, two input [[#MacAddress]], two input s16s ('''Band''' and '''ChannelNumber''') and an input [[#MessageFlagSet]]. No output.
Takes a type-0x21 input buffer, two input [[#MacAddress]], two input s16s ('''Band''' and '''ChannelNumber''') and an input [[#MessageFlagSet]]. No output.


=== RecvActionFrame ===
[20.0.0+] The input s16s were replaced with a single u16, which has the same format as [[#SetHomeChannel|SetHomeChannel]].
Takes a type-0x22 output buffer and an input [[#MessageFlagSet]]. Returns an output u32 '''Size''', two output [[#MacAddress]], two output s16s ('''Band''' and '''ChannelNumber''') and an output s32 '''LinkLevel'''.


=== SetHomeChannel ===
The first [[#MacAddress]] is the destination, the second [[#MacAddress]] is the Bssid.
Takes two input s16s '''Band''' and '''ChannelNumber'''. No output.


[20.0.0+] Now takes a total of 2-bytes of input instead of 4-bytes.
The ChannelNumber must be non-zero.


=== SetTxPower ===
[[#State|State]] must be 3-5 (AccessPointCreated/Station/StationConnected).
Takes an input s16 '''Power'''. No output.
 
=== RecvActionFrame ===
Takes a type-0x22 output buffer and an input [[#MessageFlagSet]]. Returns two output [[#MacAddress]], two output s16s ('''Band''' and '''ChannelNumber'''), an output u32 '''Size''', and an output s32 '''LinkLevel'''.
 
[20.0.0+] The output s16s were replaced with a single u16, which has the same format as [[#SetHomeChannel|SetHomeChannel]].
 
[[#EnableActionFrame|EnableActionFrame]] must be used prior to this.
 
=== SetHomeChannel ===
Takes two input s16s '''Band''' and '''ChannelNumber'''. No output.
 
[20.0.0+] Now takes an input u16 instead of two s16s, merging the two params. Bitmask 0x3FF (low 10-bits) is the ChannelNumber, while the remaining upper 6-bits is the Band. The Band is used as an array index to load the actual Band for passing to a func. Only the following Band input is valid, others return 0x0/0xFFFF: 2 - > 2400, 5 -> 5000, 6 -> 6000.
 
On NX Band must be ([20.0.0+] converted Band from the above array) 50 ([20.0.0+] 5000) or 24 ([20.0.0+] 2400).
 
The ChannelNumber must be non-zero.
 
The [[#State|State]] must be Station.
 
sdknso uses the input channel to convert to the input needed by the cmd.
 
=== SetTxPower ===
Takes an input s16 '''Power'''. No output.
 
The input must be 0x0..0xFF.
 
A state field must be non-zero.
 
The [[#State|State]] must be 2-5 (AccessPoint*/Station*).


=== ResetTxPower ===
=== ResetTxPower ===
No input/output.
No input/output.
The same state field checked by [[#SetTxPower|SetTxPower]] must be non-zero. The [[#State|State]] check is also the same as [[#SetTxPower|SetTxPower]].


== IClientProcessMonitor ==
== IClientProcessMonitor ==
Line 441: Line 521:
| 0 || RegisterClient
| 0 || RegisterClient
|}
|}
=== RegisterClient ===
Takes an input PID and an u64 pid_placeholder.
[18.0.0+] [[#CreateClientProcessMonitor|CreateClientProcessMonitor]] and RegisterClient are used by sdknso at the end of the ldn initialization functionality.
If the objptr in IClientProcessMonitor state is already set from using this cmd previously, this just returns 0.
This goes through global state to locate an entry with a matching PID, if none found 0 is returned. The objptr from the state entry is loaded, if NULL this returns 0. This obj is then incref'd and written into the IClientProcessMonitor state. When PID is 0, 0 is returned. It then locates the above state entry again with a matching PID, clearing the entry which matches. Lastly 0 is returned.
The initialization [[#InitializeWithPriority|cmds]] adds an entry to the above global state.


= ldn:u =
= ldn:u =
This is "nn::ldn::detail::IUserServiceCreator".
This is "nn::ldn::detail::IUserServiceCreator".
This has IPC max_sessions 3.
[18.0.0+] The sdknso uses SessionManager with this, where the additional session-count is 0x3.
[S2] There appears to be 2 ldn:u services, this appears to be for having separate [[#Protocol|Protocol]] permissions for NX and Ounce games. The Creator object has the same vtable for both of these. However the vtable for IUserLocalCommunicationService appears to be larger even with NX, which likely indicates there's new commands? There also appears to be 4 additional max-sessions allocated to ldn*, this is probably for one of these ldn:u services?


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 457: Line 554:
Returns an [[#IUserLocalCommunicationService]].
Returns an [[#IUserLocalCommunicationService]].


The user-process closes the IUserServiceCreator object immediately after using this cmd. Official sw ignores errors from this cmd.
The user-process closes the IUserServiceCreator object once finished with it during initialization. Official sw ignores errors from this cmd.


== IUserLocalCommunicationService ==
== IUserLocalCommunicationService ==
This is "nn::ldn::detail::IUserLocalCommunicationService".
This is "nn::ldn::detail::IUserLocalCommunicationService".
This is identical to [[#ISystemLocalCommunicationService]], except for the System-only cmd(s), and [[#Initialize_3|Initialize]]/[[#InitializeWithVersion_2|InitializeWithVersion]] differ.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 524: Line 619:
| 304 || [[#Disconnect|Disconnect]]
| 304 || [[#Disconnect|Disconnect]]
|-
|-
| 400 || [[#Initialize_3|Initialize]]
| 400 || [[#Initialize_2|Initialize]]
|-
|-
| 401 || [[#Finalize_3|Finalize]]
| 401 || [[#Finalize_2|Finalize]]
|-
|-
| 402 || [7.0.0+] [[#InitializeWithVersion_2|InitializeWithVersion]]
| 402 || [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]]
|-
|-
| 403 || [19.0.0+] [[#InitializeWithPriority|InitializeWithPriority]]
| 403 || [19.0.0+] [[#SetOperationMode|SetOperationMode]]
|-
|-
| 500 || [18.0.0+] [[#EnableActionFrame|EnableActionFrame]]
| 500 || [18.0.0+] [[#EnableActionFrame|EnableActionFrame]]
Line 546: Line 641:
| 601 || [18.0.0+] [[#ResetTxPower|ResetTxPower]]
| 601 || [18.0.0+] [[#ResetTxPower|ResetTxPower]]
|}
|}
=== Initialize ===
Takes an input PID and an u64 pid_placeholder.
This is used immediately after object creation.
With [7.0.0+] [[#InitializeWithVersion_2|InitializeWithVersion]] is used instead.
This is identical to [[#Initialize_2|Initialize]] except different params are used for the funcs called internally.
=== Finalize ===
No input/output.
This is used during service exit, prior to closing the object. Official sw will Abort if this fails.
=== InitializeWithVersion ===
Takes an input PID, an u32, and an u64 pid_placeholder.
Official sw uses hard-coded value 0x1 for the u32.
The input u32 is ignored, the impl for this cmd is identical to [[#Initialize_3|Initialize]].
This is identical to [[#InitializeWithVersion|InitializeWithVersion]] except different params are used for the funcs called internally: the u16 value is 0x5A, and the value for the second func is 1.


= ndd =
= ndd =
Line 948: Line 1,020:
= NodeInfo =
= NodeInfo =
This is "nn::ldn::NodeInfo".
This is "nn::ldn::NodeInfo".
The first node in the nodes array is always the AccessPoint (NodeId 0x0). NodeId is the index of the node in the nodes array.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 965: Line 1,039:
| 0xC || 0x21 || UserName
| 0xC || 0x21 || UserName
|-
|-
| 0x2D || 0x1 || [?+] Platform? (0 = NX, 1 = Ounce)
| 0x2D || 0x1 || [19.0.0+] Platform? (0 = NX, 1 = Ounce)
|-
|-
| 0x2E || 0x2 || LocalCommunicationVersion
| 0x2E || 0x2 || LocalCommunicationVersion
Line 1,003: Line 1,077:
| 0xC || 0x4 || Reserved
| 0xC || 0x4 || Reserved
|}
|}
* LocalCommunicationId: [[#CreateNetwork|CreateNetwork]], [[#CreateNetworkPrivate|CreateNetworkPrivate]], [[#Connect|Connect]], [[#ConnectPrivate|ConnectPrivate]] (also [[#ScanFilter|ScanFilter]] when enabled with the flag): When -1, this is overwritten with the first LocalCommunicationId from the user-process [[NACP]], if loading fails value 0 is written instead. Otherwise when not -1, if [[NACP]] loading is successful, this field must match one of the LocalCommunicationIds from there.
* SceneId: Arbitrary user data, this can be used for filtering with [[#ScanFilter|ScanFilter]] for example.


= SessionId =
= SessionId =
This is "nn::ldn::SessionId".
This is "nn::ldn::SessionId".
This is used to generate/overwrite the Ssid when needed.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,109: Line 1,188:
This is "nn::ldn::ScanFilter". This is a 0x60-byte struct with 8-byte alignment.
This is "nn::ldn::ScanFilter". This is a 0x60-byte struct with 8-byte alignment.


sdknso copies the input ScanFilter to a tmp struct on stack, which is then used with the cmd.
sdknso copies the input ScanFilter to a tmp struct on stack (with [[#ScanFilterFlag|Flag]] masking), which is then used with the cmd. sdknso only copies Bssid with [[#ScanPrivate|ScanPrivate]], with [[#Scan|Scan]] it also masks out the [[#ScanFilterFlag|Flag]] for Bssid.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,129: Line 1,208:
| 0x5C || 0x4 || [[#ScanFilterFlag|Flag]]
| 0x5C || 0x4 || [[#ScanFilterFlag|Flag]]
|}
|}
Each [[#ScanFilterFlag|Flag]] bit when set enables using the corresponding ScanFilter data. This is usually a compare with the ScanFilter data and the internal [[#NetworkInfo|NetworkInfo]] data.
* NetworkType: (ScanFilter_NetworkType & NetworkInfo_NetworkType) must be non-zero.
* Ssid: The length fields must match, then memcmp is used.
The filtering func also handles validating the Band/Channel, however these fields are internal only and are not exposed in the user ScanFilter.


= ScanFilterFlag =
= ScanFilterFlag =
Line 1,227: Line 1,313:
| 3 || SystemDebug
| 3 || SystemDebug
|}
|}
Value:
* 1-2: Broadcast Action frame data is encrypted.
* 3: Broadcast Action frame data is plaintext.
* 1: Data frames are encrypted.
* 2-3: Data frames for normal data-transfer are plaintext - the network is Open.


= SecurityConfig =
= SecurityConfig =
Line 1,244: Line 1,337:
|}
|}


Type:
= SecurityParameter =
* 1-2: Broadcast Action frame data is encrypted and is verified with SHA256.
This is "nn::ldn::SecurityParameter". This is a 0x20-byte struct with 1-byte alignment.
* 3: Broadcast Action frame data is plaintext and is verified with SHA256.
 
* 1: Data frames are encrypted.
* 2-3: Data frames for normal data-transfer are plaintext - the network is Open.
 
= SecurityParameter =
This is "nn::ldn::SecurityParameter". This is a 0x20-byte struct with 1-byte alignment.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,268: Line 1,354:
This is "nn::ldn::UserConfig". This is a 0x30-byte struct with 1-byte alignment.
This is "nn::ldn::UserConfig". This is a 0x30-byte struct with 1-byte alignment.


sdknso copies the input UserConfig to a tmp struct on stack, which is then used with the cmd.
An error is thrown if UserName+0x20 is non-zero.
 
sdknso copies the input UserConfig to a tmp struct on stack, which is then used with the cmd. Only the first 0x20-bytes are copied, with the rest cleared.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,276: Line 1,364:
! Description
! Description
|-
|-
| 0x0 || 0x21 || UserName (NUL-terminated string for the user nickname)
| 0x0 || 0x21 || UserName (NUL-terminated string for the user name)
|-
|-
| 0x21 || 0xF || Reserved (cleared to zero during the copy)
| 0x21 || 0xF || Reserved
|}
|}


Line 1,376: Line 1,464:
|-
|-
| 1 || NX
| 1 || NX
|-
| 2 || [S2]
|-
|-
| 3 || (NXAndOunce?)
| 3 || (NXAndOunce?)
|-
| 4 || [S2]
|}
|}


The Initialize* cmds configure state the same as using [[#SetProtocol|SetProtocol]] with Protocol NX.
The Initialize* cmds configure state the same as using [[#SetProtocol|SetProtocol]] with Protocol NX.


There's presumably a S2-only value which enables using Ounce-only keys. Attempting to decrypt Ounce-only ldn with the Protocol3 keys fails (including with [[#Scan|Scan]]/[[#ScanPrivate|ScanPrivate]]), with the following (with S2 hosting):
There appears to be S2-only values which enables using Ounce-only keys. The following uses new Ounce-only keys (with S2 hosting):
* In-game ldn-usage for a S2-only Application.
* In-game ldn-usage for a S2-only Application.
* Local-game-update with a S2-only Application.
* Local-game-update with a S2-only Application.
* Local-game-update for a S1-game which has a Nintendo Switch 2 Edition available, even without the S2-Edition being installed.
* Local-game-update for a S1-game which has a Nintendo Switch 2 Edition available, even without the S2-Edition being installed.
There's system-titles which [[20.0.0|use]] SetProtocol. While there's game(s) which use SetProtocol, there's no known (?) games using Protocol3 (excluding GameShare which is system).
[S2] Protocol2 and Protocol4 appear to be identical to Protocol3 except for using [[SPL_services|Ounce]] keys (?). It's unknown exactly which Protocol uses which [[SPL_services|Generation]], though presumably 2/4 is Generation 0x0/0x1? lcs (local-content-share) uses Generation 0x1, while Mario Kart World uses 0x0.
[S2] Keys are generated by passing the SHA256 hash as the KeySource to the relevant [[SPL_services|spl:ldn]] command with the above Generation, with the full hash being passed to the Ounce cmds. Only the first 0x10-bytes of the output key is used, since AES-128 is used even with Ounce.
On NX, the Protocol [[#SetProtocol|permission-bitmask]] is always set to 0xA (1 and 3). On Ounce, all services have this set to 0x1E, except for one which has it set to 0xA (which is likely the one for S1-compat). 0x1E allows additional protocol values 2 and 4.


= ActionFrameSettings =
= ActionFrameSettings =
Line 1,402: Line 1,502:
| 0x3C || 0x2 || SecurityMode
| 0x3C || 0x2 || SecurityMode
|-
|-
| 0x3E || 0x2 || PassphraseSize
| 0x3E || 0x2 || PassphraseSize (Must be 0x10-0x40)
|-
|-
| 0x40 || 0x40 || Passphrase
| 0x40 || 0x40 || Passphrase
|}
|}
SecurityMode must be 1-2. The same SecurityMode override functionality from elsewhere is used later with this.
The same LocalCommunicationId override/validation from elsewhere is used with the input as well.


= MessageFlagSet =
= MessageFlagSet =
Line 1,420: Line 1,524:
| 0 ||  
| 0 ||  
|}
|}
[[#SendActionFrame|SendActionFrame]]/[[#RecvActionFrame|RecvActionFrame]] handles bit0 the same way as the MessageFlag with lp2p [[#SendToOtherGroup|SendToOtherGroup]]/[[#RecvFromOtherGroup|RecvFromOtherGroup]].


= MacAddress =
= MacAddress =
Line 1,556: Line 1,662:
= Network protocol =
= Network protocol =
== ldn ==
== ldn ==
A beacon and Action frame are broadcasted. The SSID in the beacon is hidden (32-bytes with value 0). For [[#Scan]]/[[#ScanPrivate]] it doesn't matter if no beacon is available ([[#NetworkInfo]] is the same), as long as the Action frame is broadcasted. However, the Station will not send a probe-request during connection if no beacon is available (and therefore not attempt any communication with the AccessPoint). The beacon doesn't have any custom Nintendo data, that data is in the Acton frame.
A beacon and Action frame are broadcasted. The SSID in the beacon is hidden (32-bytes with value 0). For [[#Scan]]/[[#ScanPrivate]] it doesn't matter if no beacon is available ([[#NetworkInfo]] is the same), as long as the Action frame is broadcasted. However, the Station will not send a probe-request during connection if no beacon is available (and therefore not attempt any communication with the AccessPoint). The beacon doesn't have any custom Nintendo data, that data is in the Action frame.
 
During connection, the Station first sends a probe-request using the [[#NetworkInfo|generated]] SSID from the Action frame. If the probe-response contains the expected data for the [[#SecurityConfig]] type, the Station then proceeds to connect to the AccessPoint.


During connection, the Station first sends a probe-request using the [[#NetworkInfo|generated]] SSID from the Action frame. If the probe-response contains the expected data for the [[#SecurityConfig]] type, the Station then proceeds to connect to the AccessPoint. The key for data-frames, if [[#SecurityConfig|enabled]], is derived from a buffer containing: {[[#SecurityParameter]]+0x0} followed by {[[#SecurityConfig]] data with the specified data-size}. The [[#ActionFrame]]/data-frame keys are derived with the same func, the only difference is the input passed to this func + the passed constant data. The key derived by ldn is used directly as the static CCMP key for all data-frames (CCMP / MIC is standard). When [[#Protocol|Protocol]] is 3 the [[SPL_services|Generation]] is [[19.0.0|0x13]] instead of 0x0, for all of the previously mentioned keys derivation.
Keys are derived with: <code>GenerateAesKek(AccessKey, KeySource, Generation, Option=0); GenerateAesKey(out_key, AccessKey, {output from SHA256(data_to_hash)});</code> The key for data-frames, if [[#SecurityConfig|enabled]], is derived from a buffer containing: {[[#SecurityParameter]]+0x0} followed by {[[#SecurityConfig]] Passphrase with the specified PassphraseSize}. The [[#ActionFrame]]/data-frame keys are derived roughly the same, the only difference is the data for hashing + the [[SPL_services|KeySource]]. The key derived by ldn is used directly as the static CCMP key for all data-frames (CCMP / MIC is standard). When [[#Protocol|Protocol]] is 3 the [[SPL_services|Generation]] is [[19.0.0|0x13]] instead of 0x0, for all of the previously mentioned keys derivation.


Then the Station scans for an [[#ActionFrame]] for loading the [[#NetworkInfo]].
Then the Station scans for an [[#ActionFrame]] for loading the [[#NetworkInfo]].
Line 1,566: Line 1,674:
After Authentication the Station will scan for another [[#ActionFrame]], with frame-comparision enabled with the above frame (frame must have been updated since the previous scan). The Station locates the index for a [[#MacAddress|MacAddress]] matching itself in the [[#NetworkInfo]] [[#NodeInfo|NodeInfo]] array (the entry for the AccessPoint is skipped), throwing an error if not found. After validating the LocalCommunicationVersion, it proceeds to handle ARP setup below.
After Authentication the Station will scan for another [[#ActionFrame]], with frame-comparision enabled with the above frame (frame must have been updated since the previous scan). The Station locates the index for a [[#MacAddress|MacAddress]] matching itself in the [[#NetworkInfo]] [[#NodeInfo|NodeInfo]] array (the entry for the AccessPoint is skipped), throwing an error if not found. After validating the LocalCommunicationVersion, it proceeds to handle ARP setup below.


This does not use DHCP, each node on the network has to manually setup ARP (without sending ARP network requests) with the [[#NodeInfo|NodeInfo]] array in [[#NetworkInfo]]. [?+] After the client is [[#EthFrame|authenticated]] the host sends an ARP reply to the client (there's no ARP usage besides this).
This does not use DHCP, each node on the network has to manually setup IP-config with the [[#NodeInfo|NodeInfo]] array in [[#NetworkInfo]]. [?+] After the client is [[#EthFrame|authenticated]] ARP may be used in some cases however.


At this point standard sockets can be used over Data frames.
At this point standard sockets can be used over Data frames.
Line 1,599: Line 1,707:
| 0x4 || 0x1 || [6.0.0+] High u8 for the size.
| 0x4 || 0x1 || [6.0.0+] High u8 for the size.
|-
|-
| 0x5 || 0x1 || [?+] AuthEncryptionType, must match the type being used by the [[#Protocol|Protocol]]. 0 = plaintext ([[#Protocol|Protocol]] NX), 1 = AES-128-GCM ([[#Protocol|Protocol]] 3).
| 0x5 || 0x1 || [20.0.0+] AuthEncryptionType, must match the type being used by the [[#Protocol|Protocol]]. 0 = plaintext ([[#Protocol|Protocol]] NX), 1 = AES-128-GCM ([[#Protocol|Protocol]] non-NX).
|-
|-
| 0x6 || 0x2 || Unused, zeros.
| 0x6 || 0x2 || Unused, zeros.
Line 1,619: Line 1,727:
|}
|}


The Station sets the above size to 0x40 ([6.0.0+] if [[#NetworkInfo]]+0x13 is <3). [6.0.0+] The Authentication challenge is only used/enabled if that value is >=3, and [[#IUserLocalCommunicationService]] is being used.
The Station sets the above size to 0x40 ([6.0.0+] if [[#NetworkInfo]]+0x13 is <3) ([?+] 0x64 regardless of [[#AuthVersion]]). [6.0.0+] The Authentication challenge is only used/enabled if that value is >=3, and [[#IUserLocalCommunicationService]] is being used.


The AccessPoint sets the above size to 0x40 ([6.0.0+] 0x0 if the +0x0 [[#AuthVersion]] is <3). [6.0.0+] The AccessPoint will only use/enable the Authentication challenge when the +0x0 [[#AuthVersion]] is >=3, and [[#IUserLocalCommunicationService]] is being used. This data will not be included in the frame if the status field indicates error.
The AccessPoint sets the above size to 0x40 ([6.0.0+] 0x0 if the +0x0 [[#AuthVersion]] is <3) ([?+] 0x84 regardless of [[#AuthVersion]]). [6.0.0+] The AccessPoint will only use/enable the Authentication challenge when the +0x0 [[#AuthVersion]] is >=3, and [[#IUserLocalCommunicationService]] is being used. This data will not be included in the frame if the status field indicates error.


[6.0.0+] Support for the Authentication challenge with [[ETicket_services|es]] cmds 1501-1504 was added.
[6.0.0+] Support for the Authentication challenge with [[ETicket_services|es]] cmds 1501-1504 was added.
Line 1,641: Line 1,749:
| 0x20 || 0x2 || Big-endian LocalCommunicationVersion. Byte-swapped by the AccessPoint then copied into state. [?+] This is now ignored.
| 0x20 || 0x2 || Big-endian LocalCommunicationVersion. Byte-swapped by the AccessPoint then copied into state. [?+] This is now ignored.
|-
|-
| 0x22 || 0x1 || [?+] [[#NodeInfo|NodeInfo]] +0x2D. Copied into state by the AccessPoint. On NX the Station always sets this to 0 in the sent payload-data.
| 0x22 || 0x1 || [19.0.0+] [[#NodeInfo|NodeInfo]] +0x2D. Copied into state by the AccessPoint. On NX the Station always sets this to 0 in the sent payload-data.
|-
|-
| 0x23 || 0x1D || Zeros, unused by the AccessPoint.
| 0x23 || 0x1D || Zeros, unused by the AccessPoint.
Line 1,658: Line 1,766:
! Description
! Description
|-
|-
| 0x0 || 0x40 || Zeros. [6.0.0-?] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
| 0x0 || 0x1 || Unused, always set to 0. [S2] This is usually set to value 1?
|-
| 0x1 || 0x3F || Zeros. [6.0.0-?] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
|-
|-
| 0x40 || 0x44 || [6.0.0-?] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
| 0x40 || 0x44 || [6.0.0-?] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
Line 1,666: Line 1,776:


===== AuthVersion =====
===== AuthVersion =====
Must be 0x1-0xF (<x010 with newer versions).
Must be 0x1-0xF (<0x10 with newer versions).


[?+] When the AccessPoint is handling the [[#Authentication|Authentication]] EthFrame, the AuthVersion must be >=1 for [[#Protocol|Protocol]] NX, and >=4 for [[#Protocol|Protocol]] 3.
[?+] When the AccessPoint is handling the [[#Authentication|Authentication]] EthFrame, the AuthVersion must be >=1 for [[#Protocol|Protocol]] NX, and >=4 for [[#Protocol|Protocol]] 3.
Line 1,680: Line 1,790:
| 3 || [6.0.0+]
| 3 || [6.0.0+]
|-
|-
| 4 || ?
| 4 || [19.0.0+]
|}
|}


Line 1,717: Line 1,827:
| 0x20 || 0x1 || [[#AuthVersion]]. Copied to [[#NetworkInfo]]+0x63. When comparing with a previous frame is enabled, this must match the value from the previous frame.
| 0x20 || 0x1 || [[#AuthVersion]]. Copied to [[#NetworkInfo]]+0x63. When comparing with a previous frame is enabled, this must match the value from the previous frame.
|-
|-
| 0x21 || 0x1 || Encryption type: 1 = plaintext, 2 = AES-128-CTR, 3 = AES-128-GCM, {frames with other values are ignored by [[#Scan]]/[[#ScanPrivate]]}. Must match the type which is currently being used: with [[#Scan]]/[[#ScanPrivate]] this is determined via this field, otherwise [[#SecurityConfig]] is used to determine this.
| 0x21 || 0x1 || Encryption type: 1 = plaintext, 2 = AES-128-CTR, [20.0.0+] 3 = AES-128-GCM, {frames with other values are ignored by [[#Scan]]/[[#ScanPrivate]]}. Must match the type which is currently being used: with [[#Scan]]/[[#ScanPrivate]] this is determined via this field, otherwise [[#SecurityConfig]] is used to determine this.
|-
|-
| 0x22 || 0x2 || Big-endian u16 size for the data starting at +0x48 (+0x38 with EncryptionType3), and must match {total frame size relative to +0x0 above} - {header_size}.
| 0x22 || 0x2 || Big-endian u16 size for the data starting at +0x48 (+0x38 with EncryptionType3), and must match {total frame size relative to +0x0 above} - {header_size}.
Line 1,728: Line 1,838:
|}
|}


Using EncryptionType3 outside of [[#Scan]]/[[#ScanPrivate]] is enabled with [[#Protocol|Protocol]] 3.
Using EncryptionType3 outside of [[#Scan]]/[[#ScanPrivate]] is enabled with [[#Protocol|Protocol]] non-NX.


When encryption is enabled, the encrypted data is at +0x28 (+0x38 with EncryptionType3) with size {remaining frame size}. The key is derived from the raw 0x20-bytes at +0x0. The CTR/IV is {raw Counter above without byte-swap}, with the rest cleared to zeros. The AAD for AES-128-GCM is at +0x0 size 0x28-bytes.
When encryption is enabled, the encrypted data is at +0x28 (+0x38 with EncryptionType3) with size {remaining frame size}. The key is derived from the raw 0x20-bytes at +0x0. The CTR/IV is {raw Counter above without byte-swap}, with the rest cleared to zeros. The AAD for AES-128-GCM is at +0x0 size 0x28-bytes.
Originally [[#Scan]]/[[#ScanPrivate]] used the EncryptionType field to determine encryption handling. With [18.0.0+] these now set an internal SecurityMode field to 0 (Any) initially, then later uses the same SecurityMode override as [[#CreateNetwork|CreateNetwork]]. The internal SecurityMode field is used to determine encryption handling: Any uses the EncryptionType field like before. With non-zero the encryption handling is determined as required by the SecurityMode.


The content data at +{above_header_size} follows, which has the size specified above (which must be >=0x500 with EncryptionType1-2), where all fields are big-endian. For EncryptionType1-2:
The content data at +{above_header_size} follows, which has the size specified above (which must be >=0x500 with EncryptionType1-2), where all fields are big-endian. For EncryptionType1-2:
Line 1,777: Line 1,889:
| 0x0 || 0x10 || [[#NetworkInfo]]+0x50
| 0x0 || 0x10 || [[#NetworkInfo]]+0x50
|-
|-
| 0x10 || 0x8 || [[#NetworkInfo]]+0x478
| 0x10 || 0x8 || [[#NetworkInfo]]+0x478 [S2] The Station seems to verify that this is 0 when the Challenge is unused (ldn:s)? Throws an error if set before connecting to an [[#Protocol|Ounce]] network.
|-
|-
| 0x18 || 0x1 || [[#NetworkInfo]]+0x60
| 0x18 || 0x1 || [[#NetworkInfo]]+0x60
Line 1,816: Line 1,928:
| 0xA || 0x1 || bool IsConnected
| 0xA || 0x1 || bool IsConnected
|-
|-
| 0xB || 0x1 || [?+] [[#NodeInfo|NodeInfo]] +0x2D
| 0xB || 0x1 || [19.0.0+] [[#NodeInfo|NodeInfo]] +0x2D
|-
|-
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
Line 1,846: Line 1,958:
|}
|}


== lp2p ==
=== ActionFrame2 ===
This is used for communicating with accessories (external devices on [11.0.0+]) over local wifi. [[Mario Kart Live: Home Circuit]] uses this. [11.0.0+] [[Album_Applet|LibraryAppletPhotoViewer]] uses this.
The Action frames used by [[#SendActionFrame|SendActionFrame]]/[[#RecvActionFrame|RecvActionFrame]] have the following structure:
 
* "Fixed parameters":
A beacon is broadcasted.
** "Category code: Vendor Specific (127)"
** "OUI: 00:22:aa (Nintendo Co., Ltd.)"
* The Data starts with the following header:
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x2 || 04 00
|-
| 0x2 || 0x2 || Protocol ID, must match big-endian 0x0102.
|-
| 0x4 || 0x2 || 00 00
|-
| 0x6 || 0x2 || 00 00
|}
 
Then the actual data follows (all fields big-endian):
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x1 || [[#AuthVersion]]
|-
| 0x1 || 0x1 || EncryptionType, must match the expected type for the current SecurityMode. 1 = plaintext, 2 = AES-128-GCM.
|-
| 0x2 || 0x2 || Padding
|-
| 0x4 || 0x4 || Counter
|-
| 0x8 || 0x8 || LocalCommunicationId
|-
| 0x10 || 0x10 || Used with key derivation.
|-
| 0x20 || 0x10 || EncryptionType2: AES-128-GCM MAC tag.
|-
| 0x30 || Remaining frame size || Data payload
|}
 
The 0xC-byte IV for AES-128-GCM is the raw 4-bytes from the above Counter, with the rest cleared to zeroes. The encrypted data is at +0x30, with the remaining frame size. The AAD is the 0x20-bytes at +0x0.
 
The key is derived similar to the regular action-frame key (same KeySource), except: the Generation is always [[17.0.0|0x11]], with the following data for hashing: {big-endian LocalCommunicationId} {+0x10 size 0x10-bytes} {[[#ActionFrameSettings]] Passphrase with the specified PassphraseSize}.
 
The data payload is the data buffer for [[#SendActionFrame|SendActionFrame]]/[[#RecvActionFrame|RecvActionFrame]].
 
== lp2p ==
This is used for communicating with accessories (external devices on [11.0.0+]) over local wifi. [[Mario Kart Live: Home Circuit]] uses this. [11.0.0+] [[Album_Applet|LibraryAppletPhotoViewer]] uses this.
 
A beacon is broadcasted.


Action frames are only sent when done so by [[#SendToOtherGroup]] (other than the Epigram one mentioned below).
Action frames are only sent when done so by [[#SendToOtherGroup]] (other than the Epigram one mentioned below).
Line 1,995: Line 2,160:
|-
|-
| 0x0 || {remaining size} || Plaintext user-data.
| 0x0 || {remaining size} || Plaintext user-data.
|}
= Notes =
The following services are accessible to ldn:
* arp:r, bsd:s, btm, es, fatal:u, ifcfg, lm, nifm:s, pl:s, set:sys, spl:mig, wlan
[S2] Access to btm and spl:mig were replaced with bt:sys and spl:ldn.
[S2] Various objects/state have the same size/layout as S1, generally?(Other than IPC-related differences) There's also stack differences.
== Code-region Memory Layout ==
=== S2 20.5.0 ===
This is the codebin-region layout for S2 ldn 20.5.0, exact starting version unknown. BuildId is "DC456FA4...".
The on-NX note is for the equivalent memregion location/size, memregion-size/contents compared to NX may vary.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Permissions
! Description
|-
| 0x0 || 0xE1000 || --X || .text
|-
| 0xE1000 || 0x3D000 || R-- || RO-region
|-
| 0x11E000 || 0x3A000 || RW || On NX this is at 0xEF000. The main ExpHeap is at +0x2000, on NX it's at +0x1000.
|-
| 0x158000 || 0x8000 || non-RW || On NX this is at 0x123000.
|-
| 0x160000 || 0x8000 || RW || On NX this is at 0x12B000.
|-
| 0x168000 || 0xF000 || non-RW || On NX this is at 0x139000.
|-
| 0x177000 || 0x3000 || RW || On NX this is at 0x13F000.
|-
| 0x17A000 || 0x24000 || non-RW || On NX this is at 0x14B000.
|-
| 0x19E000 || 0x4E000 || RW || On NX this is at 0x16F000.
|-
| 0x1EC000 || 0x2000 || -- || On NX this is at 0x1BD000.
|-
| 0x1EE000 || 0x2000 || {accessible} || On NX this is at 0x1BF000.
|-
| 0x1EF000 || 0x5000 || -- || On NX this is at 0x1E2000.
|-
| 0x1F4000 || 0x21000 || {accessible} || On NX this is at 0x1EA000.
|-
| 0x218000 || || -- || Likely end going by NX.
|}
|}


[[Category:Services]]
[[Category:Services]]