LDN services: Difference between revisions

 
(141 intermediate revisions by 3 users not shown)
Line 3: Line 3:
= 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 8: Line 12:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#CreateMonitorService]]
| 0 || [[#CreateMonitorService|CreateMonitorService]]
|}
|}


Line 23: Line 27:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#GetStateForMonitor]]
| 0 || [[#GetState|GetState]]
|-
|-
| 1 || [[#GetNetworkInfoForMonitor]]
| 1 || [[#GetNetworkInfo|GetNetworkInfo]]
|-
|-
| 2 || [[#GetIpv4AddressForMonitor]]
| 2 || [[#GetIpv4Address|GetIpv4Address]]
|-
|-
| 3 || [[#GetDisconnectReasonForMonitor]]
| 3 || [[#GetDisconnectReason|GetDisconnectReason]]
|-
|-
| 4 || [[#GetSecurityParameterForMonitor]]
| 4 || [[#GetSecurityParameter|GetSecurityParameter]]
|-
|-
| 5 || [[#GetNetworkConfigForMonitor]]
| 5 || [[#GetNetworkConfig|GetNetworkConfig]]
|-
|-
| 100 || [[#InitializeMonitor]]
| 100 || [[#Initialize]]
|-
|-
| 101 || [[#FinalizeMonitor]]
| 101 || [[#Finalize]]
|}
|}


=== GetStateForMonitor ===
=== GetState ===
No input, returns an output u32.
No input, returns an output 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.


=== GetNetworkInfoForMonitor ===
=== GetNetworkInfo ===
Takes a type-0x1A output buffer containing a [[#NetworkInfo]].
Takes a type-0x1A output buffer containing a [[#NetworkInfo]].


=== GetIpv4AddressForMonitor ===
=== GetIpv4Address ===
No input, returns an output [[#Ipv4Address]] and a [[#SubnetMask]].
No input, returns an output [[#Ipv4Address]] and a [[#SubnetMask]].


=== GetDisconnectReasonForMonitor ===
=== GetDisconnectReason ===
No input, returns an output s16.
No input, returns an output s16.


Line 58: Line 62:
This just returns 0.
This just returns 0.


=== GetSecurityParameterForMonitor ===
=== GetSecurityParameter ===
No input, returns an output [[#SecurityParameter]].
No input, returns an output [[#SecurityParameter]].


This is not exposed by sdknso.
This is not exposed by sdknso.


=== GetNetworkConfigForMonitor ===
=== GetNetworkConfig ===
No input, returns an output [[#NetworkConfig]].
No input, returns an output [[#NetworkConfig]].


This is not exposed by sdknso.
This is not exposed by sdknso.


=== InitializeMonitor ===
=== Initialize ===
No input/output.
No input/output.


Line 75: Line 79:
This just returns 0.
This just returns 0.


=== FinalizeMonitor ===
=== Finalize ===
No input/output.
No input/output.


Line 84: Line 88:
= ldn:s =
= ldn:s =
This is "nn::ldn::detail::ISystemServiceCreator".
This is "nn::ldn::detail::ISystemServiceCreator".
[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 89: Line 95:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#CreateSystemLocalCommunicationService]]
| 0 || [[#CreateSystemLocalCommunicationService|CreateSystemLocalCommunicationService]]
|-
| 1 || [18.0.0+] [[#CreateClientProcessMonitor|CreateClientProcessMonitor]]
|}
|}


== CreateSystemLocalCommunicationService ==
== CreateSystemLocalCommunicationService ==
Returns an [[#ISystemLocalCommunicationService]].
No input. Returns an [[#ISystemLocalCommunicationService]].
 
The user-process closes the ISystemServiceCreator object once finished with it during initialization. Official sw ignores errors from this cmd.


The user-process closes the ISystemServiceCreator object immediately after using this cmd. Official sw ignores errors from this cmd.
== CreateClientProcessMonitor ==
No input. Returns an [[#IClientProcessMonitor]].


== ISystemLocalCommunicationService ==
== ISystemLocalCommunicationService ==
Line 104: Line 115:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#GetState]]
| 0 || [[#GetState_2|GetState]]
|-
| 1 || [[#GetNetworkInfo_2|GetNetworkInfo]]
|-
|-
| 1 || [[#GetNetworkInfo]]
| 2 || [[#GetIpv4Address_2|GetIpv4Address]]
|-
|-
| 2 || [[#GetIpv4Address]]
| 3 || [[#GetDisconnectReason_2|GetDisconnectReason]]
|-
|-
| 3 || [[#GetDisconnectReason]]
| 4 || [[#GetSecurityParameter_2|GetSecurityParameter]]
|-
|-
| 4 || [[#GetSecurityParameter]]
| 5 || [[#GetNetworkConfig_2|GetNetworkConfig]]
|-
|-
| 5 || [[#GetNetworkConfig]]
| 100 || [[#GetStateChangeEvent|GetStateChangeEvent]]
|-
|-
| 100 || [[#AttachStateChangeEvent]]
| 101 || [[#GetNetworkInfoAndHistory|GetNetworkInfoAndHistory]]
|-
|-
| 101 || [[#GetNetworkInfoLatestUpdate]]
| 102 || [[#Scan|Scan]]
|-
|-
| 102 || [[#Scan]]
| 103 || [[#ScanPrivate|ScanPrivate]]
|-
|-
| 103 || [[#ScanPrivate]]
| 104 || [5.0.0+] [[#SetWirelessControllerPolicy|SetWirelessControllerPolicy]]
|-
|-
| 104 || [5.0.0+] [[#SetWirelessControllerRestriction]]
| 105 || [13.1.0+] [[#SetWirelessAudioPolicy|SetWirelessAudioPolicy]]
|-
|-
| 200 || [[#OpenAccessPoint]]
| 106 || [18.0.0+] [[#SetProtocol|SetProtocol]]
|-
|-
| 201 || [[#CloseAccessPoint]]
| 200 || [[#OpenAccessPoint|OpenAccessPoint]]
|-
|-
| 202 || [[#CreateNetwork]]
| 201 || [[#CloseAccessPoint|CloseAccessPoint]]
|-
|-
| 203 || [[#CreateNetworkPrivate]]
| 202 || [[#CreateNetwork|CreateNetwork]]
|-
|-
| 204 || [[#DestroyNetwork]]
| 203 || [[#CreateNetworkPrivate|CreateNetworkPrivate]]
|-
|-
| 205 || [[#Reject]]
| 204 || [[#DestroyNetwork|DestroyNetwork]]
|-
|-
| 206 || [[#SetAdvertiseData]]
| 205 || [[#Reject|Reject]]
|-
|-
| 207 || [[#SetStationAcceptPolicy]]
| 206 || [[#SetAdvertiseData|SetAdvertiseData]]
|-
|-
| 208 || [[#AddAcceptFilterEntry]]
| 207 || [[#SetStationAcceptPolicy|SetStationAcceptPolicy]]
|-
|-
| 209 || [[#ClearAcceptFilter]]
| 208 || [[#AddAcceptFilterEntry|AddAcceptFilterEntry]]
|-
|-
| 300 || [[#OpenStation]]
| 209 || [[#ClearAcceptFilter|ClearAcceptFilter]]
|-
|-
| 301 || [[#CloseStation]]
| 300 || [[#OpenStation|OpenStation]]
|-
|-
| 302 || [[#Connect]]
| 301 || [[#CloseStation|CloseStation]]
|-
|-
| 303 || [[#ConnectPrivate]]
| 302 || [[#Connect|Connect]]
|-
|-
| 304 || [[#Disconnect]]
| 303 || [[#ConnectPrivate|ConnectPrivate]]
|-
|-
| 400 || [[#InitializeSystem]]
| 304 || [[#Disconnect|Disconnect]]
|-
|-
| 401 || [[#FinalizeSystem]]
| 400 || [[#Initialize_2|Initialize]]
|-
|-
| 402 || [4.0.0+] [[#SetOperationMode]]
| 401 || [[#Finalize_2|Finalize]]
|-
|-
| 403 || [7.0.0+] [[#InitializeSystem2]]
| 402 || [4.0.0+] [[#SetOperationMode|SetOperationMode]]
|}
 
=== GetState ===
No input, returns an output u32.
 
sdknso implements this by <code>return</code>ing the u32, with 0 being returned on error / when service isn't initialized.
 
{| class="wikitable" border="1"
|-
|-
!  Value
| 403 || [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]]
!  Description
|-
|-
| 0 || None
| 404 || [19.0.0+] [[#InitializeWithPriority|InitializeWithPriority]]
|-
|-
| 1 || Initialized
| 500 || [18.0.0+] [[#EnableActionFrame|EnableActionFrame]]
|-
|-
| 2 || AccessPointOpened
| 501 || [18.0.0+] [[#DisableActionFrame|DisableActionFrame]]
|-
|-
| 3 || AccessPointCreated
| 502 || [18.0.0+] [[#SendActionFrame|SendActionFrame]]
|-
|-
| 4 || StationOpened
| 503 || [18.0.0+] [[#RecvActionFrame|RecvActionFrame]]
|-
|-
| 5 || StationConnected
| 505 || [18.0.0+] [[#SetHomeChannel|SetHomeChannel]]
|-
|-
| 6 || Error
| 600 || [18.0.0+] [[#SetTxPower|SetTxPower]]
|-
|-
| 601 || [18.0.0+] [[#ResetTxPower|ResetTxPower]]
|}
|}
=== GetState ===
No input, returns an output [[#State]].
sdknso implements this by <code>return</code>ing the u32, with 0 being returned on error / when service isn't initialized.


=== GetNetworkInfo ===
=== GetNetworkInfo ===
Line 198: Line 208:


=== GetDisconnectReason ===
=== GetDisconnectReason ===
No input, returns an output s16.
No input, returns an output [[#DisconnectReason]].


sdknso implements this by <code>return</code>ing the s16 as a s32, with -1 being returned on error.
sdknso implements this by <code>return</code>ing the s16 as a s32, with -1 being returned on error.
{| class="wikitable" border="1"
|-
!  Value
!  Description
|-
| -1 || See above.
|-
| 0 || None
|-
| 1 || User
|-
| 2 || SystemRequest
|-
| 3 || DestroyedByAdmin
|-
| 4 || DestroyedBySystemRequest
|-
| 5 || Admin
|-
| 6 || SignalLost
|}


=== GetSecurityParameter ===
=== GetSecurityParameter ===
Line 230: Line 218:
No input, returns an output [[#NetworkConfig]].
No input, returns an output [[#NetworkConfig]].


=== AttachStateChangeEvent ===
=== GetStateChangeEvent ===
No input, returns an output Event handle.
No input, returns an output Event handle.


sdknso uses EventClearMode=1 with this. sdknso will Abort if this cmd fails.
sdknso uses EventClearMode=1 with this. sdknso will Abort if this cmd fails.


This is signaled when the data returned by [[#GetNetworkInfo]]/[[#GetNetworkInfoLatestUpdate]] is updated.
This is signaled when the data returned by [[#GetNetworkInfo]]/[[#GetNetworkInfoAndHistory]] is updated.


=== GetNetworkInfoLatestUpdate ===
=== GetNetworkInfoAndHistory ===
Takes a type-0x1A output buffer containing a [[#NetworkInfo]] and a type-0xA output buffer containing an array of [[#NodeLatestUpdate]].
Takes a type-0x1A output buffer containing a [[#NetworkInfo]] and a type-0xA output buffer containing an array of [[#NodeLatestUpdate]].


Line 260: Line 248:
See [[#Scan]].
See [[#Scan]].


=== SetWirelessControllerRestriction ===
=== SetWirelessControllerPolicy ===
Takes an input [[#WirelessControllerRestriction]], no output.
Takes an input [[#WirelessControllerRestriction]], no output.


Line 266: Line 254:


The input value is written into state.
The input value is written into state.
=== SetWirelessAudioPolicy ===
Takes an input [[#BluetoothAudioDeviceConnectableMode]], no output.
=== SetProtocol ===
Takes an input u32, no output.
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.
[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 cmd_input must be non-zero. BIT(cmd_input) must be set in a state-field, otherwise a separate Result is returned.
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 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.


=== OpenAccessPoint ===
=== OpenAccessPoint ===
Line 284: Line 294:
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), value 1 is used, 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 348: Line 358:
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]].


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.
[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 357: Line 367:
See [[#Connect]].
See [[#Connect]].


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.
[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 364: Line 374:
[[#GetState|State]] must be 5, this cmd eventually sets the State to value 4.
[[#GetState|State]] must be 5, this cmd eventually sets the State to value 4.


=== InitializeSystem ===
=== Initialize ===
Takes an input PID and an u64 pid_placeholder.
Takes an input PID and an u64 pid_placeholder.


This is used immediately after object creation.
This is used immediately after object creation.


With [7.0.0+] [[#InitializeSystem2]] is used instead.
On old sysvers the cmd impl for User/System are identical, except different params are used for the funcs called internally.


=== FinalizeSystem ===
With [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]] is used instead. The cmd impl for Initialize uses [[#InitializeWithVersion|InitializeWithVersion]] with version=0.
 
=== Finalize ===
No input/output.
No input/output.


Line 387: Line 399:
The input value is written into state.
The input value is written into state.


=== InitializeSystem2 ===
=== InitializeWithVersion ===
Takes an input PID, an u32, and an u64 pid_placeholder.
Takes an input PID, a s32 version, 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 [[#InitializeSystem]].
The version must not be negative.


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).
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 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 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.
On old sysvers the cmd impl for User/System are identical, except different params are used for the funcs called internally: with User the u16 value is 0x5A, and the value for the second func is 1. With newer sysvers the cmd impl is now identical.


[[#GetState|State]] must be 0, this cmd eventually sets the State to value 1.
[[#GetState|State]] must be 0, this cmd eventually sets the State to value 1.
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 ===
Takes an input PID, a s32 version, a s32, 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.
=== EnableActionFrame ===
Takes an input [[#ActionFrameSettings]]. No output.
[[#State|State]] must be Initialized.
=== DisableActionFrame ===
No input/output.
[[#State|State]] must be Initialized.
=== SendActionFrame ===
Takes a type-0x21 input buffer, two input [[#MacAddress]], two input s16s ('''Band''' and '''ChannelNumber''') and an input [[#MessageFlagSet]]. No output.
[20.0.0+] The input s16s were replaced with a single u16, which has the same format as [[#SetHomeChannel|SetHomeChannel]].
The first [[#MacAddress]] is the destination, the second [[#MacAddress]] is the Bssid.
The ChannelNumber must be non-zero.
[[#State|State]] must be 3-4 (AccessPointCreated/Station).
=== 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 ===
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 ==
This is "nn::ldn::detail::IClientProcessMonitor".
This was added with [18.0.0+].
{| class="wikitable" border="1"
|-
! Cmd || Name
|-
| 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".
[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 407: Line 519:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#CreateUserLocalCommunicationService]]
| 0 || [[#CreateUserLocalCommunicationService|CreateUserLocalCommunicationService]]
|-
| 1 || [18.0.0+] [[#CreateClientProcessMonitor|CreateClientProcessMonitor]]
|}
|}


Line 413: Line 527:
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]]/[[#Initialize2]] differ.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 424: Line 536:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#GetState]]
| 0 || [[#GetState_2|GetState]]
|-
|-
| 1 || [[#GetNetworkInfo]]
| 1 || [[#GetNetworkInfo_2|GetNetworkInfo]]
|-
|-
| 2 || [[#GetIpv4Address]]
| 2 || [[#GetIpv4Address_2|GetIpv4Address]]
|-
|-
| 3 || [[#GetDisconnectReason]]
| 3 || [[#GetDisconnectReason_2|GetDisconnectReason]]
|-
|-
| 4 || [[#GetSecurityParameter]]
| 4 || [[#GetSecurityParameter_2|GetSecurityParameter]]
|-
|-
| 5 || [[#GetNetworkConfig]]
| 5 || [[#GetNetworkConfig_2|GetNetworkConfig]]
|-
|-
| 100 || [[#AttachStateChangeEvent]]
| 100 || [[#GetStateChangeEvent|GetStateChangeEvent]]
|-
|-
| 101 || [[#GetNetworkInfoLatestUpdate]]
| 101 || [[#GetNetworkInfoAndHistory|GetNetworkInfoAndHistory]]
|-
|-
| 102 || [[#Scan]]
| 102 || [[#Scan|Scan]]
|-
|-
| 103 || [[#ScanPrivate]]
| 103 || [[#ScanPrivate|ScanPrivate]]
|-
|-
| 104 || [5.0.0+] [[#SetWirelessControllerRestriction]]
| 104 || [5.0.0+] [[#SetWirelessControllerPolicy|SetWirelessControllerPolicy]]
|-
|-
| 200 || [[#OpenAccessPoint]]
| 105 || [13.1.0+] [[#SetWirelessAudioPolicy|SetWirelessAudioPolicy]]
|-
|-
| 201 || [[#CloseAccessPoint]]
| 106 || [18.0.0+] [[#SetProtocol|SetProtocol]]
|-
|-
| 202 || [[#CreateNetwork]]
| 200 || [[#OpenAccessPoint|OpenAccessPoint]]
|-
|-
| 203 || [[#CreateNetworkPrivate]]
| 201 || [[#CloseAccessPoint|CloseAccessPoint]]
|-
|-
| 204 || [[#DestroyNetwork]]
| 202 || [[#CreateNetwork|CreateNetwork]]
|-
|-
| 205 || [[#Reject]]
| 203 || [[#CreateNetworkPrivate|CreateNetworkPrivate]]
|-
|-
| 206 || [[#SetAdvertiseData]]
| 204 || [[#DestroyNetwork|DestroyNetwork]]
|-
|-
| 207 || [[#SetStationAcceptPolicy]]
| 205 || [[#Reject|Reject]]
|-
|-
| 208 || [[#AddAcceptFilterEntry]]
| 206 || [[#SetAdvertiseData|SetAdvertiseData]]
|-
|-
| 209 || [[#ClearAcceptFilter]]
| 207 || [[#SetStationAcceptPolicy|SetStationAcceptPolicy]]
|-
|-
| 300 || [[#OpenStation]]
| 208 || [[#AddAcceptFilterEntry|AddAcceptFilterEntry]]
|-
|-
| 301 || [[#CloseStation]]
| 209 || [[#ClearAcceptFilter|ClearAcceptFilter]]
|-
|-
| 302 || [[#Connect]]
| 300 || [[#OpenStation|OpenStation]]
|-
|-
| 303 || [[#ConnectPrivate]]
| 301 || [[#CloseStation|CloseStation]]
|-
|-
| 304 || [[#Disconnect]]
| 302 || [[#Connect|Connect]]
|-
|-
| 400 || [[#Initialize]]
| 303 || [[#ConnectPrivate|ConnectPrivate]]
|-
|-
| 401 || [[#Finalize]]
| 304 || [[#Disconnect|Disconnect]]
|-
|-
| 402 || [7.0.0+] [[#Initialize2]]
| 400 || [[#Initialize_2|Initialize]]
|-
| 401 || [[#Finalize_2|Finalize]]
|-
| 402 || [7.0.0+] [[#InitializeWithVersion|InitializeWithVersion]]
|-
| 403 || [19.0.0+] [[#SetOperationMode|SetOperationMode]]
|-
| 500 || [18.0.0+] [[#EnableActionFrame|EnableActionFrame]]
|-
| 501 || [18.0.0+] [[#DisableActionFrame|DisableActionFrame]]
|-
| 502 || [18.0.0+] [[#SendActionFrame|SendActionFrame]]
|-
| 503 || [18.0.0+] [[#RecvActionFrame|RecvActionFrame]]
|-
| 505 || [18.0.0+] [[#SetHomeChannel|SetHomeChannel]]
|-
| 600 || [18.0.0+] [[#SetTxPower|SetTxPower]]
|-
| 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+] [[#Initialize2]] is used instead.
This is identical to [[#InitializeSystem]] 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.
This is identical to [[#FinalizeSystem]].
=== Initialize2 ===
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]].
This is identical to [[#InitializeSystem2]] 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 569: Line 676:


= lp2p:app, lp2p:sys =
= lp2p:app, lp2p:sys =
These are "nn::lp2p::detail::INetworkServiceCreator".
These are "nn::lp2p::detail::ISfServiceCreator".


These were added with [9.0.0+].
These were added with [9.0.0+].


lp2p:app is used by [[Mario Kart Live: Home Circuit]].
lp2p:app is used by [[Mario Kart Live: Home Circuit]]. lp2p:sys is used by [[Album_Applet|LibraryAppletPhotoViewer]] with [11.0.0+].


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 579: Line 686:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#CreateNetworkService]]
| 0 || [[#CreateNetworkService|CreateNetworkService]]
|-
|-
| 8 || [[#CreateNetworkServiceMonitor]]
| 8 || [[#CreateNetworkServiceMonitor|CreateNetworkServiceMonitor]]
|}
|}


== CreateNetworkService ==
== CreateNetworkService ==
Takes an input u32, an u64 pid_reserved, a PID, returns an output [[#INetworkService]].
Takes a PID-descriptor, a reserved input u64 and an input u32. Returns an output [[#ISfService]].


The input u32 must be value 0x1.
The input u32 must be value 0x1.


== CreateNetworkServiceMonitor ==
== CreateNetworkServiceMonitor ==
Takes an input u64 pid_reserved, a PID, returns an output [[#INetworkServiceMonitor]].
Takes a PID-descriptor and a reserved input u64. Returns an output [[#ISfServiceMonitor]].


== INetworkService ==
== ISfService ==
This is "nn::lp2p::detail::INetworkService".
This is "nn::lp2p::detail::ISfService".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 599: Line 706:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#Initialize|Initialize]]
| 0 || [[#Initialize_4|Initialize]]
|-
|-
| [9.0.0-9.0.1] 256 || [[#AttachNetworkInterfaceStateChangeEvent]]
| 256 || [9.0.0-9.0.1] [[#AttachNetworkInterfaceStateChangeEvent|AttachNetworkInterfaceStateChangeEvent]]
|-
|-
| [9.0.0-9.0.1] 264 || [[#GetNetworkInterfaceLastError]]
| 264 || [9.0.0-9.0.1] [[#GetNetworkInterfaceLastError|GetNetworkInterfaceLastError]]
|-
|-
| [9.0.0-9.0.1] 272 || [[#GetRole]]
| 272 || [9.0.0-9.0.1] [[#GetRole|GetRole]]
|-
|-
| [9.0.0-9.0.1] 280 || [[#GetAdvertiseData]]
| 280 || [9.0.0-9.0.1] [[#GetAdvertiseData|GetAdvertiseData]]
|-
|-
| [9.0.0-9.0.1] 288 || [[#GetGroupInfo]]
| 288 || [9.0.0-9.0.1] [[#GetGroupInfo|GetGroupInfo]]
|-
|-
| [9.0.0-9.0.1] 296 || [[#Join]]
| 296 || [9.0.0-9.0.1] [[#GetGroupInfo2|GetGroupInfo2]]
|-
|-
| [9.0.0-9.0.1] 304 || [[#GetGroupOwner]]
| 304 || [9.0.0-9.0.1] [[#GetGroupOwner|GetGroupOwner]]
|-
|-
| [9.0.0-9.0.1] 312 || [[#GetIpConfig]]
| 312 || [9.0.0-9.0.1] [[#GetIpConfig|GetIpConfig]]
|-
|-
| [9.0.0-9.0.1] 320 || [[#Leave]]
| 320 || [9.0.0-9.0.1] [[#GetLinkLevel|GetLinkLevel]]
|-
|-
| 512 || [[#Scan]]
| 512 || [[#Scan_2|Scan]]
|-
|-
| 768 || [[#CreateGroup]]
| 768 || [[#CreateGroup|CreateGroup]]
|-
|-
| 776 || [[#DestroyGroup]]
| 776 || [[#DestroyGroup|DestroyGroup]]
|-
|-
| 784 || [[#SetAdvertiseData]]
| 784 || [[#SetAdvertiseData|SetAdvertiseData]]
|-
|-
| 1536 || [[#SendToOtherGroup]]
| 1536 || [[#SendToOtherGroup|SendToOtherGroup]]
|-
|-
| 1544 || [[#RecvFromOtherGroup]]
| 1544 || [[#RecvFromOtherGroup|RecvFromOtherGroup]]
|-
|-
| 1552 || [[#AddAcceptableGroupId]]
| 1552 || [[#AddAcceptableGroupId|AddAcceptableGroupId]]
|-
|-
| 1560 || [9.1.0+] [[#RemoveAcceptableGroupId]]
| 1560 || [9.1.0+] [[#ClearAcceptableGroupId|ClearAcceptableGroupId]]
|}
|}


Line 644: Line 751:


=== Scan ===
=== Scan ===
Takes a type-0x19 input buffer containing a 0x200-byte struct, a type-0x22 output buffer containing an array of [[#ScanResult]], returns an output s32 total_out.
Takes a type-0x19 input buffer containing a [[#GroupInfo]] and a type-0x22 output buffer containing an array of [[#ScanResult]]. Returns an output s32 '''TotalOut'''.


=== CreateGroup ===
=== CreateGroup ===
Takes a type-0x31 input buffer containing a [[#GroupInfo]], no output.
Takes a type-0x31 input buffer containing a [[#GroupInfo]]. No output.


[[Mario Kart Live: Home Circuit|mklive]] uses the following string with this: "Failed to create a group: %08X".
[[Mario Kart Live: Home Circuit|mklive]] uses the following string with this: "Failed to create a group: %08X".


This is only available if a group wasn't already created.
The [[#GetRole|role]] must be 0. This eventually sets the [[#GetRole|role]] to value 1.


=== DestroyGroup ===
=== DestroyGroup ===
No input/output.
No input/output.


This destroys the previously [[#CreateGroup|created]] group. If no group was previously created, this just returns 0.
This destroys the previously [[#CreateGroup|created]] group. If no group was previously created ([[#GetRole|role]] is not 1), this just returns 0.


=== SetAdvertiseData ===
=== SetAdvertiseData ===
Takes a type-0x21 input buffer, no output.
Takes a type-0x21 input buffer. No output.


The buffer size must be <=0x80.
The buffer size must be <=0x80. The [[#GetRole|role]] must be <=1.


A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the buffer data as "scan advertise data".
A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the buffer data as "scan advertise data".


=== SendToOtherGroup ===
=== SendToOtherGroup ===
Takes an input [[#MacAddress_2|MacAddress]], a [[#GroupId]], a s16 frequency, a s16 channel, an u32 flags, a type-0x21 input buffer, no output.
Takes an input [[#MacAddress_2|MacAddress]], a [[#GroupId]], a s16 '''Frequency''', a s16 '''Channel''', an u32 '''MessageFlag''' and a type-0x21 input buffer. No output.


The buffer size must be <=0x400.
The buffer size must be <=0x400.


The s16s must be >=1.
The MacAddress must be non-zero. The s16s must be >=1.


Flags is only used for selecting which func to call internally, via bit0.
Only bit0 is used from flags: clear = block until the data can be sent, set = return error when the data can't be sent.


A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the buffer data as "Action frame".
A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the buffer data as "Action frame".


This is only available when a group was previously [[#CreateGroup|created]].
The [[#GetRole|role]] must be non-zero. The error from [[#GetNetworkInterfaceLastError]] will be returned if it's set.
 
[11.0.0+] [[#GroupInfo]]+0x8A must be value 2, otherwise an error is returned.


This sends an Action frame to the specified [[#GroupId]], with the specified destination [[#MacAddress_2|MacAddress]] (can be a broadcast address).
This sends an Action frame to the specified [[#GroupId]], with the specified destination [[#MacAddress_2|MacAddress]] (can be a broadcast address).
Line 683: Line 792:


=== RecvFromOtherGroup ===
=== RecvFromOtherGroup ===
Takes an input u32 flags, a type-0x22 output buffer, returns a [[#MacAddress_2|MacAddress]], an u16, a s16, an u32 out_size, a s32.
Takes an input u32 '''MessageFlag''' and a type-0x22 output buffer. Returns a [[#MacAddress_2|MacAddress]], an u16 '''Frequency''', a s16 '''Channel''', an u32 '''OutSize''' and a s32.
 
The OutSize is the original size used for copying to the output buffer, before it's clamped to the output-buffer size.


The out_size is the original size used for copying to the output buffer, before it's clamped to the output-buffer size.
Only bit0 is used from MessageFlag: clear = block until data is available, set = return error when data is not available.


Flags is only used for selecting which func to call internally, via bit0.
When data is not available, the error from [[#GetNetworkInterfaceLastError]] will be returned if it's set.


This is only available when a group was previously [[#CreateGroup|created]].
The [[#GetRole|role]] must be non-zero.


This receives an Action frame. This will block until data is available (?).
This receives an Action frame.


=== AddAcceptableGroupId ===
=== AddAcceptableGroupId ===
Takes an input [[#GroupId]], no output.
Takes an input [[#GroupId]]. No output.


=== RemoveAcceptableGroupId ===
=== ClearAcceptableGroupId ===
No input/output.
No input/output.


== INetworkServiceMonitor ==
== ISfServiceMonitor ==
This is "nn::lp2p::detail::INetworkServiceMonitor".
This is "nn::lp2p::detail::ISfServiceMonitor".


This interface has no commands, until [9.1.0+] which added actual commands.
This interface has no commands, until [9.1.0+] which added actual commands.
Line 708: Line 819:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#Initialize_2|Initialize]]
| 0 || [[#Initialize_5|Initialize]]
|-
|-
| 256 || [[#AttachNetworkInterfaceStateChangeEvent]]
| 256 || [[#AttachNetworkInterfaceStateChangeEvent|AttachNetworkInterfaceStateChangeEvent]]
|-
|-
| 264 || [[#GetNetworkInterfaceLastError]]
| 264 || [[#GetNetworkInterfaceLastError|GetNetworkInterfaceLastError]]
|-
|-
| 272 || [[#GetRole]]
| 272 || [[#GetRole|GetRole]]
|-
|-
| 280 || [[#GetAdvertiseData]]
| 280 || [[#GetAdvertiseData|GetAdvertiseData]]
|-
|-
| 281 || [[#GetAdvertiseData2]]
| 281 || [[#GetAdvertiseData2|GetAdvertiseData2]]
|-
|-
| 288 || [[#GetGroupInfo]]
| 288 || [[#GetGroupInfo|GetGroupInfo]]
|-
|-
| 296 || [[#Join]]
| 296 || [[#GetGroupInfo2|GetGroupInfo2]]
|-
|-
| 304 || [[#GetGroupOwner]]
| 304 || [[#GetGroupOwner|GetGroupOwner]]
|-
|-
| 312 || [[#GetIpConfig]]
| 312 || [[#GetIpConfig|GetIpConfig]]
|-
|-
| 320 || [[#Leave]]
| 320 || [[#GetLinkLevel|GetLinkLevel]]
|-
|-
| 328 || [[#AttachJoinEvent]]
| 328 || [[#AttachJoinEvent|AttachJoinEvent]]
|-
|-
| 336 || [[#GetMembers]]
| 336 || [[#GetMembers|GetMembers]]
|}
|}


Line 741: Line 852:


=== AttachNetworkInterfaceStateChangeEvent ===
=== AttachNetworkInterfaceStateChangeEvent ===
No input, returns an output Event handle with EventClearMode=0.
No input. Returns an output Event handle with EventClearMode=0.


=== GetNetworkInterfaceLastError ===
=== GetNetworkInterfaceLastError ===
Line 747: Line 858:


=== GetRole ===
=== GetRole ===
No input, returns an output u8.
No input. Returns an output u8.


=== GetAdvertiseData ===
=== GetAdvertiseData ===
Takes a type-0x22 output buffer, returns 2 output u16s.
Takes a type-0x22 output buffer. Returns two output u16s.


Validates that the [[#GetRole|role]] is value 0x2, then copies data from state into the output buffer. The first output u16 is the size used for the memcpy, the second u16 is the original size from state.
Validates that the [[#GetRole|role]] is value 2, then copies data from state into the output buffer. The first output u16 is the size used for the memcpy, the second u16 is the original size from state.


=== GetAdvertiseData2 ===
=== GetAdvertiseData2 ===
Takes a type-0x22 output buffer, returns 2 output u16s.
Takes a type-0x22 output buffer. Returns two output u16s.


This is identical to [[#GetAdvertiseData]] except this doesn't run the role validation.
This is identical to [[#GetAdvertiseData]] except this doesn't run the role validation.
Line 764: Line 875:
Validates that the [[#GetRole|role]] is non-zero, then copies the struct from state into the output buffer.
Validates that the [[#GetRole|role]] is non-zero, then copies the struct from state into the output buffer.


=== Join ===
=== GetGroupInfo2 ===
Takes a type-0x32 output buffer containing a [[#GroupInfo]] and a type-0x31 input buffer containing a [[#GroupInfo]].
Takes a type-0x32 output buffer containing a [[#GroupInfo]] and a type-0x31 input buffer containing a [[#GroupInfo]].


Line 770: Line 881:


=== GetGroupOwner ===
=== GetGroupOwner ===
No input, returns an output 0x80-bytes [[#NodeInfo_2|NodeInfo]].
No input. Returns an output [[#NodeInfo_2|NodeInfo]].


Validates that the [[#GetRole|role]] is non-zero, then copies the data from state to output.
Validates that the [[#GetRole|role]] is non-zero, then copies the data from state to output.
Line 781: Line 892:
+0x20 is the <code>struct sockaddr</code> IP address, +0x40 is the <code>struct sockaddr</code> subnet-mask, +0x60 is the <code>struct sockaddr</code> gateway(?). The address for the last one is set to localhost.
+0x20 is the <code>struct sockaddr</code> IP address, +0x40 is the <code>struct sockaddr</code> subnet-mask, +0x60 is the <code>struct sockaddr</code> gateway(?). The address for the last one is set to localhost.


=== Leave ===
=== GetLinkLevel ===
No input, returns an output u32.
No input. Returns an output u32.


=== AttachJoinEvent ===
=== AttachJoinEvent ===
No input, returns an output Event handle with EventClearMode=0.
No input. Returns an output Event handle with EventClearMode=0.


=== GetMembers ===
=== GetMembers ===
Takes a type-0x22 output buffer containing an array of [[#NodeInfo_2|NodeInfo]], returns an output s32 total_out.
Takes a type-0x22 output buffer containing an array of [[#NodeInfo_2|NodeInfo]]. Returns an output s32 '''TotalOut'''.


Validates that the [[#GetRole|role]] is value 0x1. Then any entries from state which are available are copied into the output array buffer, if there's space available.
Validates that the [[#GetRole|role]] is value 1. Then any entries from state which are available are copied into the output array buffer, if there's space available. A maximum of 8 entries can be returned.


A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the array data as "connected members".
A string in [[Mario Kart Live: Home Circuit|mklive]] refers to the array data as "connected members".


= lp2p:m =
= lp2p:m =
This is "nn::lp2p::detail::IMonitorServiceCreator".
This is "nn::lp2p::monitor::detail::ISfMonitorServiceCreator".


This was added with [9.1.0+].
This was added with [9.1.0+].
Line 803: Line 914:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#CreateMonitorService]]
| 0 || [[#CreateMonitorService|CreateMonitorService]]
|}
|}


== CreateMonitorService ==
== CreateMonitorService ==
Takes a PID, a total of 0x10-bytes of input, and returns an [[#IMonitorService]].
Takes a PID-descriptor, a reserved input u64 and an input u64. Returns an [[#ISfMonitorService]].


== IMonitorService ==
== ISfMonitorService ==
This is "nn::lp2p::detail::IMonitorService".
This is "nn::lp2p::monitor::detail::ISfMonitorService".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 816: Line 927:
! Cmd || Name
! Cmd || Name
|-
|-
| 0 || [[#Initialize_3|Initialize]]
| 0 || [[#Initialize_6|Initialize]]
|-
|-
| 288 || [[#GetGroupInfo]]
| 288 || [[#GetGroupInfo|GetGroupInfo]]
|-
|-
| 320 || [[#Leave]]
| 320 || [[#GetLinkLevel|GetLinkLevel]]
|}
|}


=== Initialize ===
=== Initialize ===
Returns 0.
Returns 0.
= State =
This is "nn::ldn::State".
{| class="wikitable" border="1"
|-
!  Value
!  Description
|-
| 0 || None
|-
| 1 || Initialized
|-
| 2 || AccessPoint
|-
| 3 || AccessPointCreated
|-
| 4 || Station
|-
| 5 || StationConnected
|-
| 6 || Error
|}


= Ipv4Address =
= Ipv4Address =
Line 849: Line 983:
! Description
! Description
|-
|-
| 0x0 || 0x1 || Length excluding NUL-terminator, must be 0x1-0x20.
| 0x0 || 0x1 || Length (excluding NUL-terminator, must be 0x1-0x20)
|-
| 0x1 || 0x21 || Raw (SSID string including NUL-terminator, str[{above length}] must be 0)
|}
 
= MacAddress =
This is "nn::ldn::MacAddress". This is a 6-byte struct with 1-byte alignment.
 
= 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"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || [[#Ipv4Address|Ipv4Address]]
|-
| 0x4 || 0x6 || [[#MacAddress|MacAddress]]
|-
| 0xA || 0x1 || NodeId
|-
| 0xB || 0x1 || IsConnected
|-
| 0xC || 0x21 || UserName
|-
| 0x2D || 0x1 || [19.0.0+] Platform? (0 = NX, 1 = Ounce)
|-
| 0x2E || 0x2 || LocalCommunicationVersion
|-
|-
| 0x1 || 0x21 || SSID string including NUL-terminator, str[{above length}] must be 0.
| 0x30 || 0x10 || Reserved
|}
|}


= NetworkInfo =
= NodeLatestUpdate =
This is "nn::ldn::NetworkInfo". This is a 0x480-byte struct. The data at +0x50 is another struct.
This is "nn::ldn::NodeLatestUpdate". This is a 0x8-byte struct.
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x1 || StateChange
|-
| 0x1 || 0x7 || Reserved
|}


The fields listed as Reserved (besides the fields before +0x10) are cleared during the memset and are not written to again afterwards, with cmds which return NetworkInfo.
= IntentId =
This is "nn::ldn::IntentId".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 869: Line 1,046:
| 0x8 || 0x2 || Reserved
| 0x8 || 0x2 || Reserved
|-
|-
| 0xA || 0x2 || Arbitrary user data which can be used for filtering with [[#ScanFilter]].
| 0xA || 0x2 || SceneId
|-
|-
| 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 =
This is "nn::ldn::SessionId".
This is used to generate/overwrite the Ssid when needed.
{| class="wikitable" border="1"
|-
|-
| 0x10 || 0x10 || Last 0x10-bytes of [[#SecurityParameter]]. NetworkId which is used to generate/overwrite the [[#Ssid]]. With [[#Scan]]/[[#ScanPrivate]], this is only done after filtering when +0x4B is value 0x2. The converted Ssid is a 0x20-byte lowercase hex string version of the input NetworkId.
! Offset
! Size
! Description
|-
| 0x0 || 0x10 || Random
|}
 
= NetworkId =
This is "nn::ldn::NetworkId". This is a 0x20-byte struct.
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x10 || [[#IntentId|IntentId]]
|-
| 0x10 || 0x10 || [[#SessionId|SessionId]]
|}
 
= CommonNetworkInfo =
This is "nn::ldn::CommonNetworkInfo". This is a 0x30-byte struct.
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
|-
| 0x20 || 0x6 || [[#MacAddress|MacAddress]]
| 0x0 || 0x6 || [[#MacAddress|Bssid]]
|-
|-
| 0x26 || 0x22 || [[#Ssid]]
| 0x6 || 0x22 || [[#Ssid|Ssid]]
|-
|-
| 0x48 || 0x2 || s16 NetworkChannel
| 0x28 || 0x2 || Channel
|-
|-
| 0x4A || 0x1 || s8 LinkLevel
| 0x2A || 0x1 || LinkLevel
|-
|-
| 0x4B || 0x1 || Set to hard-coded value 0x2 with output structs, except with [[#Scan]]/[[#ScanPrivate]] which can also set value 0x1 in certain cases. 0x1 = normal network without an [[#ActionFrame]], 0x2 = LDN network with a valid [[#ActionFrame]].
| 0x2B || 0x1 || NetworkType
|-
|-
| 0x4C || 0x4 || Padding
| 0x2C || 0x4 || Reserved
|}
 
= LdnNetworkInfo =
This is "nn::ldn::LdnNetworkInfo". This is a 0x430-byte struct.
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
|-
| 0x50 || 0x10 || First 0x10-bytes of [[#SecurityParameter]].
| 0x0 || 0x10 || ServerRandom
|-
|-
| 0x60 || 0x2 || Same as [[#SecurityConfig]]+0x0.
| 0x10 || 0x2 || SecurityMode
|-
|-
| 0x62 || 0x1 || [[#AcceptPolicy]]
| 0x12 || 0x1 || StationAcceptPolicy
|-
|-
| 0x63 || 0x1 || Only set with [[#Scan]]/[[#ScanPrivate]], when +0x4B is value 0x2. See [[#ActionFrame]].
| 0x13 || 0x1 || Version
|-
|-
| 0x64 || 0x2 || Padding
| 0x14 || 0x2 || Reserved
|-
|-
| 0x66 || 0x1 || Maximum participants, for the [[#NodeInfo|NodeInfo]] array.
| 0x16 || 0x1 || NodeCountMax
|-
|-
| 0x67 || 0x1 || ParticipantNum, number of set entries in the [[#NodeInfo|NodeInfo]] array. If +0x4B is not 0x2, ParticipantNum should be handled as if it's 0.
| 0x17 || 0x1 || NodeCount
|-
|-
| 0x68 || 0x200(0x40*8) || Array of [[#NodeInfo|NodeInfo]] with 8 entries, starting with the AccessPoint node.
| 0x18 || 0x200 (0x40 * 8) || [[#NodeInfo|Nodes]]
|-
|-
| 0x268 || 0x2 || Reserved
| 0x218 || 0x2 || Reserved
|-
|-
| 0x26A || 0x2 || AdvertiseData size
| 0x21A || 0x2 || AdvertiseDataSize
|-
|-
| 0x26C || 0x180 || AdvertiseData
| 0x21C || 0x180 || AdvertiseData
|-
|-
| 0x3EC || 0x8C || Reserved
| 0x39C || 0x8C || Reserved
|-
|-
| 0x478 || 0x8 || [6.0.0+] Random AuthenticationId. Set to the output from [[ETicket_services|es]] cmd1501 during network creation.
| 0x428 || 0x8 || [6.0.0-?] Challenge (set to the output from [[ETicket_services|es]] cmd1501 during network creation) ([?+] only used internally, not exposed in LdnNetworkInfo anymore)
|}
|}


= ScanFilter =
= NetworkInfo =
This is "nn::ldn::ScanFilter". This is a 0x60-byte struct with 8-byte alignment.
This is "nn::ldn::NetworkInfo". This is a 0x480-byte struct.


sdknso copies the input ScanFilter to a tmp struct on stack, which is then used with the cmd.
The fields listed as Reserved (besides the fields before +0x10) are cleared during the memset and are not written to again afterwards, with cmds which return NetworkInfo.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 925: Line 1,151:
! Description
! Description
|-
|-
| 0x0 || 0x8 || When enabled, this will be overwritten if it's -1. The data written for this is the first [[NACP_Format|LocalCommunicationId]] for the user-process loaded via [[Glue_services|arp:r]], if loading fails value 0 is written instead. During filtering if enabled, u8 [[#NetworkInfo]]+0x4B must match 0x2, and this ScanFilter field must match [[#NetworkInfo]]+0x0.
| 0x0 || 0x20 || [[#NetworkId|NetworkId]]
|-
|-
| 0x8 || 0x2 || Padding
| 0x20 || 0x30 || [[#CommonNetworkInfo|Common]]
|-
|-
| 0xA || 0x2 || During filtering if enabled, u8 [[#NetworkInfo]]+0x4B must match 0x2, and this ScanFilter field must match [[#NetworkInfo]]+0xA.
| 0x50 || 0x430 || [[#LdnNetworkInfo|Ldn]]
|}
 
= ScanFilter =
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 (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"
|-
|-
| 0xC || 0x4 || Padding
! Offset
! Size
! Description
|-
|-
| 0x10 || 0x10 || During filtering if enabled, u8 [[#NetworkInfo]]+0x4B must match 0x2, and this ScanFilter data must match [[#NetworkInfo]]+0x10.
| 0x0 || 0x20 || [[#NetworkId|NetworkId]]
|-
|-
| 0x20 || 0x4 || When enabled, must be <=0x3, and during filtering must match u8 [[#NetworkInfo]]+0x4B.
| 0x20 || 0x4 || NetworkType
|-
|-
| 0x24 || 0x6 || [[#MacAddress|MacAddress]]. Only copied with [[#ScanPrivate]]. During filtering if enabled, this must match [[#NetworkInfo]]+0x20.
| 0x24 || 0x6 || [[#MacAddress|Bssid]]
|-
|-
| 0x2A || 0x22 || [[#Ssid]]. During filtering if enabled, this must match [[#NetworkInfo]]+0x26 (the [[#Ssid]] there must be valid for this as well). The strings are compared, without verifying the length field in [[#Ssid]] matches.
| 0x2A || 0x22 || [[#Ssid|Ssid]]
|-
|-
| 0x4C || 0x10 || Cleared to zero for the tmp struct.
| 0x4C || 0x10 || Reserved
|-
|-
| 0x5C || 0x4 || Flags. Masked with 0x37 for [[#Scan]], with [[#ScanPrivate]] this is masked with 0x3F.
| 0x5C || 0x4 || [[#ScanFilterFlag|Flag]]
|}
|}


Flags:
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 =
This is "nn::ldn::ScanFilterFlag".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
! Bit
! Value
! Description
! Description
|-
| 0x0 || None
|-
| 0x1 || LocalCommunicationId
|-
| 0x2 || SessionId
|-
| 0x4 || NetworkType
|-
|-
| 0 || When set, enables using ScanFilter+0.
| 0x8 || Bssid
|-
|-
| 1 || When set, enables using ScanFilter+0x10.
| 0x10 || Ssid
|-
|-
| 2 || When set, enables using ScanFilter+0x20.
| 0x20 || SceneId
|-
|-
| 3 || When set, enables using the ScanFilter [[#MacAddress|MacAddress]].
| 0x21 || IntentId
|-
|-
| 4 || When set, enables using the ScanFilter [[#Ssid]].
| 0x23 || NetworkId
|-
|-
| 5 || When set, enables using ScanFilter+0xA.
| 0x3F || All
|}
|}


Line 977: Line 1,229:
! Description
! Description
|-
|-
| 0x0 || 0x8 || LocalCommunicationId. Same as [[#NetworkInfo]]+0x0. [[#CreateNetwork]]/[[#CreateNetworkPrivate]]/[[#Connect]]/[[#ConnectPrivate]]: When -1, this is overwritten with the first [[NACP_Format|LocalCommunicationId]] for the user-process loaded via [[Glue_services|arp:r]], if loading fails value 0 is written instead. Otherwise when not -1, if control.nacp loading is successful with [[Glue_services|arp:r]], this field must match one of the LocalCommunicationIds from there otherwise an error is thrown.
| 0x0 || 0x10 || [[#IntentId|IntentId]]
|-
|-
| 0x8 || 0x2 || Cleared to zero during the copy.
| 0x10 || 0x2 || Channel
|-
|-
| 0xA || 0x2 || Same as [[#NetworkInfo]]+0xA.
| 0x12 || 0x1 || NodeCountMax
|-
|-
| 0xC || 0x4 || Cleared to zero during the copy.
| 0x13 || 0x1 || Reserved
|-
|-
| 0x10 || 0x2 || s16 Channel, can be zero. Same as [[#NetworkInfo]]+0x48.
| 0x14 || 0x2 || LocalCommunicationVersion
|-
|-
| 0x12 || 0x1 || s8. Same as [[#NetworkInfo]]+0x66. [[#CreateNetwork]]/[[#CreateNetworkPrivate]]: Must be 0x1-0x8.
| 0x16 || 0xA || Reserved
|}
 
= WirelessControllerRestriction =
This is "nn::ldn::WirelessControllerRestriction". This is an u32 enum.
 
This is used to determine the value passed to [[BTM_services|btm]] SetWlanMode.
 
{| class="wikitable" border="1"
|-
|-
| 0x13 || 0x1 || Cleared to zero during the copy.
!  Value
!  Description
|-
|-
| 0x14 || 0x2 || Same as [[#NetworkInfo]]+0x96 (LocalCommunicationVersion from the first [[#NodeInfo|NodeInfo]]). Must not be negative. [[#Connect]]/[[#ConnectPrivate]]: This must match the value for the AccessPoint LocalCommunicationVersion.
| 0 || Disabled
|-
|-
| 0x16 || 0xA || Cleared to zero during the copy.
| 1 || Enabled
|}
|}


= NodeLatestUpdate =
= WirelessAudioRestriction =
This is "nn::ldn::NodeLatestUpdate". This is a 0x8-byte struct.
This is "nn::ldn::WirelessAudioRestriction". This is an u32 enum.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
! Offset
! Value
! Size
! Description
! Description
|-
|-
| 0x0 || 0x1 || The field in state is reset to zero by [[#GetNetworkInfoLatestUpdate]] after loading it. Official apps checks whether this is non-zero.
| 0 || Disabled
|-
|-
| 0x1 || 0x7 || Not initialized with [[#GetNetworkInfoLatestUpdate]].
| 1 || Enabled
|}
|}


= WirelessControllerRestriction =
= SecurityMode =
This is "nn::ldn::WirelessControllerRestriction". This is an u32 enum.
This is "nn::ldn::SecurityMode". This is an u32 enum.
 
This is used to determine the value passed to [[BTM_services|btm]] SetWlanMode.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,020: Line 1,278:
!  Description
!  Description
|-
|-
| 0 ||  
| 0 || Any
|-
| 1 || Product
|-
| 2 || Debug
|-
|-
| 1 || This is the default.
| 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,034: Line 1,303:
! Description
! Description
|-
|-
| 0x0 || 0x2 || Type, a default of value 1 can be used here. Overwritten by [[#CreateNetwork]]/[[#CreateNetworkPrivate]] and [[#Connect]]/[[#ConnectPrivate]]. The value used internally by these cmds must be 1-3.
| 0x0 || 0x2 || [[#SecurityMode|SecurityMode]] (overwritten by [[#CreateNetwork]]/[[#CreateNetworkPrivate]] and [[#Connect]]/[[#ConnectPrivate]], the value used internally by these cmds must be 1-3)
|-
|-
| 0x2 || 0x2 || Data size. Must be 0x10-0x40.
| 0x2 || 0x2 || PassphraseSize (must be 0x10-0x40)
|-
|-
| 0x4 || 0x40 || Data, used with key derivation.
| 0x4 || 0x40 || Passphrase (used with key derivation)
|}
|}
Type:
* 1-2: Broadcast Action frame data is encrypted and is verified with SHA256.
* 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 =
= SecurityParameter =
Line 1,057: Line 1,319:
! Description
! Description
|-
|-
| 0x0 || 0x10 || Data, used with the same key derivation as [[#SecurityConfig]].
| 0x0 || 0x10 || ServerRandom (used with the same key derivation as [[#SecurityConfig]])
|-
|-
| 0x10 || 0x10 || NetworkId, see [[#NetworkInfo]].
| 0x10 || 0x10 || [[#SessionId|SessionId]]
|}
|}


Line 1,065: Line 1,327:
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.
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,073: Line 1,335:
! Description
! Description
|-
|-
| 0x0 || 0x20 || NUL-terminated string for the user nickname.
| 0x0 || 0x21 || UserName (NUL-terminated string for the user name)
|-
|-
| 0x20 || 0x10 || Cleared to zero during the copy.
| 0x21 || 0xF || Reserved
|}
|}


Line 1,091: Line 1,353:
| 0x4 || 0x6 || [[#MacAddress|MacAddress]]
| 0x4 || 0x6 || [[#MacAddress|MacAddress]]
|-
|-
| 0xA || 0x2 || Padding
| 0xA || 0x2 || Reserved
|}
|}


Line 1,102: Line 1,364:
!  Description
!  Description
|-
|-
| 0 || Allow all.
| 0 || AlwaysAccept
|-
|-
| 1 || Deny all.
| 1 || AlwaysReject
|-
|-
| 2 || Blacklist, addresses in the [[#AddAcceptFilterEntry|list]] are not allowed.
| 2 || BlackList (addresses in the [[#AddAcceptFilterEntry|list]] are not allowed)
|-
|-
| 3 || Whitelist, only addresses in the [[#AddAcceptFilterEntry|list]] are allowed.
| 3 || WhiteList (only addresses in the [[#AddAcceptFilterEntry|list]] are allowed)
|}
|}


= MacAddress =
= ConnectOption =
This is "nn::ldn::MacAddress". This is a 6-byte struct with 1-byte alignment.
This is "nn::ldn::ConnectOption". This is an u32 bitmask.
 
There's two versions of the sdknso funcs for [[#Connect]]/[[#ConnectPrivate]]: the version where the ConnectOption isn't user-specified uses a default value of 0x1 for it, with the same ShowError code without the bit0 check.
 
When bit0 here is set after using the above cmds, the sdknso funcs will use [[Error_Applet|ShowError]] with the returned Result if: (rc & 0x3FE1FF) == 0xE0CB.


= NodeInfo =
This must be <=0x1, besides this validation ConnectOption is ignored by [[#Connect]]/[[#ConnectPrivate]].
This is "nn::ldn::NodeInfo".


The fields listed as Reserved are cleared during the memset and are not written to again afterwards, with cmds which return [[#NetworkInfo]].
= DisconnectReason =
This is "nn::ldn::DisconnectReason".


{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
! Offset
! Value
! Size
! Description
! Description
|-
|-
| 0x0 || 0x4 || [[#Ipv4Address]]
| -1 || Unknown
|-
|-
| 0x4 || 0x6 || [[#MacAddress|MacAddress]]
| 0 || None
|-
|-
| 0xA || 0x1 || s8 ID / index
| 1 || DisconnectedByUser
|-
|-
| 0xB || 0x1 || IsConnected
| 2 || DisconnectedBySystem
|-
|-
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
| 3 || DestroyedByUser
|-
|-
| 0x2C || 0x2 || Reserved
| 4 || DestroyedBySystem
|-
|-
| 0x2E || 0x2 || s16 LocalCommunicationVersion
| 5 || Rejected
|-
|-
| 0x30 || 0x10 || Reserved
| 6 || SignalLost
|}
|}
= ConnectOption =
This is "nn::ldn::ConnectOption". This is an u32 bitmask.
There's two versions of the sdknso funcs for [[#Connect]]/[[#ConnectPrivate]]: the version where the ConnectOption isn't user-specified uses a default value of 0x1 for it, with the same ShowError code without the bit0 check.
When bit0 here is set after using the above cmds, the sdknso funcs will use [[Error_Applet|ShowError]] with the returned Result if: (rc & 0x3FE1FF) == 0xE0CB.
This must be <=0x1, besides this validation ConnectOption is ignored by [[#Connect]]/[[#ConnectPrivate]].


= OperationMode =
= OperationMode =
Line 1,163: Line 1,419:
!  Description
!  Description
|-
|-
| 0 || This is the default.
| 0 || Stable
|-
|-
| 1 ||  
| 1 || HighSpeed
|}
|}
= Protocol =
This is "nn::ldn::Protocol". This is an u32 enum.
{| class="wikitable" border="1"
|-
!  Value
!  Description
|-
| 0 || Default
|-
| 1 || NX
|-
| 3 || (NXAndOunce?)
|}
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):
* In-game ldn-usage for 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.
= ActionFrameSettings =
This is "nn::ldn::ActionFrameSettings". This is a 0x80-byte struct.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x8 || LocalCommunicationId
|-
| 0x8 || 0x34 || Reserved
|-
| 0x3C || 0x2 || SecurityMode
|-
| 0x3E || 0x2 || PassphraseSize (Must be 0x10-0x40)
|-
| 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 =
This is "nn::ldn::MessageFlagSet". This is a BitFlagSet object for [[#MessageFlag]].
= MessageFlag =
This is "nn::ldn::MessageFlag".
{| class="wikitable" border="1"
|-
!  Value
!  Description
|-
| 0 ||
|}
[[#SendActionFrame|SendActionFrame]]/[[#RecvActionFrame|RecvActionFrame]] handles bit0 the same way as the MessageFlag with lp2p [[#SendToOtherGroup|SendToOtherGroup]]/[[#RecvFromOtherGroup|RecvFromOtherGroup]].


= MacAddress =
= MacAddress =
Line 1,193: Line 1,511:
This is "nn::lp2p::GroupInfo". This is a 0x200-byte struct.
This is "nn::lp2p::GroupInfo". This is a 0x200-byte struct.


[[Mario Kart Live: Home Circuit|mklive]] sets the SSID to a string generated from random data, the key-data is also set to random data.
[[Mario Kart Live: Home Circuit|mklive]] sets the SSID to a string generated from random data.
 
[[#Scan_2|Scan]] only uses the following fields for the cmd input struct: SupportedPlatform/Priority, Frequency/Channel, and PresharedKeyBinarySize/PresharedKey.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,201: Line 1,521:
! Description
! Description
|-
|-
| 0x0 || 0x10 || When zero, this is set to randomly-generated data.
| 0x0 || 0x10 || Wrapped master key. When zero, set to randomly-generated data. This is decrypted with a "static AES key" and used to derive the 4 encryption keys for the session.
|-
| 0x10 || 0x8 || LocalCommunicationId. When zero, the value from control.nacp is loaded. This is later validated by [[#Join]]/[[#CreateGroup]] the same way as the [[#NetworkConfig]] field. Used during key derivation to derive keys B and D.
|-
| 0x18 || 0x6 || [[#GroupId]] ("GROUP ID  (BSSID)"). When zero, the default is used. The default should be used here: an error is thrown if the data here doesn't match the output from [[WLAN_services|wlan:lcl]] cmd2.
|-
| 0x1E || 0x21 || ServiceName ("GROUP NAME (SSID)"). NUL-terminated string. See below.
|-
|-
| 0x10 || 0x8 || LocalCommunicationId. When zero, the value from control.nacp is loaded.
| 0x3F || 0x1 || s8 Flags count. Must be <=0x3F.
|-
|-
| 0x18 || 0x6 || "GROUP ID  (BSSID)" [[#GroupId]]. When zero, the default is used.
| 0x40 || 0x40 || Array of s8 with the above count. Each entry value must be <=0x3F. Each entry is an array index used to load a set of flags from a global array with the specified index. global_flags are also masked with flags loaded from here. User-processes use entryval=1 as the default, with [11.0.0+] entryval=0 can be used for standard WPA2-PSK (see +0x8A).
|-
|-
| 0x1E || 0x21 || "GROUP NAME (SSID)" NUL-terminated string. If the SSID is invalid, a new SSID is generated, however in this case the original SSID must contain a '-' character.
| 0x80 || 0x1 || SupportedPlatform. Must match value 1. 0 is PlatformIdNX, 1 is PlatformIdRcd.
|-
|-
| 0x3F || 0x1 || s8 Flags_mask count.
| 0x81 || 0x1 || MemberCountMax. s8, Must be <=0x8. During group creation this is passed to [[WLAN_services|wlan:lcl]] cmd40, when this is value 0 a default of value 1 is passed. During group-creation when the below +0x88 field is not value 0x2, the passed [[BTM_services#SetWlanMode|WlanMode]] is <code>x81_field_val > 3</code>.
|-
|-
| 0x40 || {above count} || Array of s8 with the above count. Each entry is an array index used to load a set of flags from a global array with the specified index. loaded_flags below is masked with the flags loaded here. global_flags are also masked with flags loaded from here.
| 0x82 || 0x1 ||  
|-
|-
| 0x84 || 0x2 || Wifi frequency: 24 = 2.4GHz, 50 = 5.0GHz.
| 0x84 || 0x2 || Frequency. Wifi frequency: 24 = 2.4GHz, 50 = 5GHz.
|-
|-
| 0x86 || 0x2 || "CHANNEL" s16. 0 = use default, otherwise this must be one of the following depending on the frequency field:
| 0x86 || 0x2 || s16 Channel ("CHANNEL"). Wifi channel number. 0 = use default, otherwise this must be one of the following depending on the frequency field:
* 24: 1, 6, 11.
* 24: 1, 6, 11.
* 50: 36, 40, 44, 48.
* 50: 36, 40, 44, 48.
|-
|-
| 0x8A || 0x1 || Security type. 0 = use defaults, 1 = plaintext, 2 = encrypted.
| 0x88 || 0x1 || NetworkMode. Used during group-creation to determine the [[BTM_services#SetWlanMode|WlanMode]] to use. When this is value 0x2, mode=3 is used, otherwise it's determined via the +0x81 field.
|-
| 0x89 || 0x1 || PerformanceRequirement.
|-
|-
| 0x8B || 0x1 ||  
| 0x8A || 0x1 || Security type, used during key derivation. 0 = use defaults, 1 = plaintext, 2 = encrypted. [11.0.0+] 3: Standard WPA2-PSK.
|-
|-
| 0x8E || 0x1 || Bool flag, controls whether the SSID is hidden.
| 0x8B || 0x1 || StaticAesKeyIndex. s8, used as the array-index for selecting the KeySource used with [[SPL_services#GenerateAesKek|GenerateAesKek]] during key derivation. Should be 1-2, otherwise GenerateAesKek is skipped and zeros are used for the AccessKey instead.
|-
|-
| 0x8F || 0x1 || If zero, a default value of 0x20 is used.
| 0x8D || 0x1 || Priority. Must match one of the following, depending on the used service (doesn't apply to [[#Join]]): 55 = SystemPriority (lp2p:sys), 90 = ApplicationPriority (lp2p:app and lp2p:sys).
|-
|-
| 0x10F || 0x1 || Flags count.
| 0x8E || 0x1 || StealthEnabled. Bool flag, controls whether the SSID is hidden.
|-
|-
| 0x110 || {above count} || Array of s8 with the above count for total entries. The flags are loaded by: <nowiki>"loaded_flags |= BIT(s8_entryvalue)"</nowiki>, with each entry. Afterwards, loaded_flags is masked with 0x7. loaded_flags must be non-zero.
| 0x8F || 0x1 || If zero, a default value of 0x20 is used.
|-
|-
| 0x1C0 || 0x1 || Key data size. Must be 0x20.
| 0x1C0 || 0x1 || PresharedKeyBinarySize. Must be 0x20 for PresharedKeyBinary. [11.0.0+] With WPA2-PSK, this must be value 1.
|-
|-
| 0x1C1 || 0x20 || Key data.
| 0x1C1 || 0x3F ([9.0.0-10.2.0] 0x20) || PresharedKey. Used to derive encryption keys A and C. [11.0.0+] With WPA2-PSK, this is the passphrase string (length must be at least 8).
|}
|}


The above loaded_flags are used when +0x8A is 0. global_flags are loaded from global data. Flags:
In order for the ServiceName to be valid without a new one being generated, the following checks must pass:
* It loops through the characters in the string, looking for the first '_' character:
** The loop will exit once a '_' character is found.
** The character must be '-', or alphanumeric (lowercase/uppercase), otherwise the function will immediately return failure.
** The loop will also exit once string_pos is >19, in which case the function will also immediately return failure.
* Then it checks the 11 characters which follow the above:
** The character must be hex: '0'-'9', or 'A-F' / 'a-'f.
* The following character must be a NUL-terminator.
* The last hex character above, then the characters for the whole string prior to the last hex character are summed. return sum % 0x2B == 0. u32 is used for these calculations. (Return success when sum is a multiple of 0x2B, otherwise return failure)
 
If the above fails, then the following runs, otherwise it just returns 0:
* It loops through the characters in the string.
** The character must be '-', or alphanumeric (lowercase/uppercase), otherwise the function will immediately return failure.
** The loop will exit once string_pos>20 is reached, or when a NUL-terminator is reached.
* Once finished, success is returned if string_pos-1 is <20, otherwise failure is returned (which also immediately occurs if the first character is a NUL-terminator).
 
If the above fails, an error is returned, otherwise a new ServiceName is generated:
* Up to 20 characters are copied from the original ServiceName to the output ServiceName, stopping once the limit is reached or when a NUL-terminator is reached.
* '_' is appended to the string.
* <code>nn::util::TSNPrintf({strptr following the above character}, {remaining size}, "%02X%02X%02X%02X%02X", [[#GroupId|GroupId_byte3]], [[#GroupId|GroupId_byte4]], [[#GroupId|GroupId_byte5]], ([[SPL_services#IsDevelopment|IsDevelopment]] ? 0x80 : 0) | 0x1, 0);</code>
* Then the last character is set to the output from a calling a function:
** All string characters which were already written are summed same way as above. Then: <code>return character_lookup_table[sum % 0x2B];</code> (If the length passed to this function is 0, this will instead just return character_lookup_table[0])
*** character_lookup_table contains 0x2B entries: [V-A][k-a][5-0][Z-W].
 
loaded_flags are first loaded from elsewhere, then masked with the above flags when available. loaded_flags are used when +0x8A is 0. global_flags are loaded from global data. These flags are only used with [[#CreateGroup]]/[[#Join]]. Flags (note that the following was updated with [11.0.0+], and differs from below):
* Bit2 clear:
* Bit2 clear:
** global_flags must be non-zero, and loaded_flags bit1 must be set.
** global_flags must be non-zero, and loaded_flags bit1 must be set.
Line 1,258: Line 1,610:
! Description
! Description
|-
|-
| 0x18 || 0x6 || [[#MacAddress_2|MacAddress]]?
| 0x0 || 0x200 || [[#GroupInfo]]
|-
| 0x86 || 0x1 ||
|-
|-
| 0x200 || 0x1 ||  
| 0x200 || 0x1 ||  
|-
|-
| 0x206 || 0x2 || Size of the following data.
| 0x206 || 0x2 || AdvertiseData size.
|-
|-
| 0x208 || 0x80 || Data with the size specified above. Advertise data?
| 0x208 || 0x80 || AdvertiseData
|}
|}


= 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. 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).
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.
 
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,281: Line 1,633:
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]].
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).


At this point standard sockets can be used over Data frames.
At this point standard sockets can be used over Data frames.
Line 1,310: Line 1,662:
| 0x2 || 0x1 || Status. 0 = success, non-zero = error.
| 0x2 || 0x1 || Status. 0 = success, non-zero = error.
|-
|-
| 0x3 || 0x1 || [2.0.0+] bool flag. The AccessPoint verifies that this is not set. Always set to 1 by the AccessPoint in the response. The Station only uses this when the [[#AuthVersion]] is >=2.
| 0x3 || 0x1 || [2.0.0+] bool flag. The AccessPoint verifies that this is not set. Always set to 1 by the AccessPoint in the response. [2.0.0-?] The Station only uses this when the [[#AuthVersion]] is >=2.
|-
|-
| 0x4 || 0x1 || [6.0.0+] High u8 for the size.
| 0x4 || 0x1 || [6.0.0+] High u8 for the size.
|-
|-
| 0x5 || 0x3 || Unused, zeros.
| 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]] 3).
|-
| 0x6 || 0x2 || Unused, zeros.
|-
|-
| 0x8 || 0x20 || [[#NetworkInfo]]+0, must match the corresponding data in [[#NetworkInfo]] when the receiving node verifies this. With the  
| 0x8 || 0x20 || [[#NetworkInfo]]+0, must match the corresponding data in [[#NetworkInfo]] when the receiving node verifies this. With the  
Line 1,322: Line 1,676:
AccessPoint->Station frame, the Station verifies that this matches the data previously sent to the AccessPoint.
AccessPoint->Station frame, the Station verifies that this matches the data previously sent to the AccessPoint.
|-
|-
| 0x38 || 0x10 || See below.
| 0x38 || 0x10 || AuthEncryptionType1: Used for key derivation.
Station->AccessPoint: The Station sets this to random data. Unused by the AccessPoint (besides the above), except for copying into the response.
 
AccessPoint->Station: +0x38 from the data originally sent by the Station. The Station verifies that this matches the previously sent data.
|-
| 0x48 || 0x10 || Only present with AuthEncryptionType1: AES-128-GCM MAC tag.
|-
|-
| 0x48 || || Frame-specific data, with the above size. The total frame size - {offset of the start of this data in the frame} must match the above size.
| 0x48 (0x58 with AuthEncryptionType1) || || Frame-specific payload data, with the above size. The total frame size - {offset of the start of this data in the frame} must match the above size.
|}
|}


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.


Station->AccessPoint frame, relative to +0x0 above (frame size depends on whether +0xAC is enabled):
AuthEncryptionType1: The key is derived essentially the same as the data-frame CCMP key, except the input data for hashing is the 0x10-bytes at +0x38 (this also only supports using [[SPL_services|Generation]] 0x13, returning immediately if the input param indicates otherwise due to the [[#Protocol|Protocol]]). The encrypted AES-128-GCM data starts at +0x58 with the above size. The 0xC-bytes IV is at +0x0, the AAD is at +0x0 size 0x48-bytes.


The AccessPoint will not respond to frames where the source mac-address is unrecognized.
The AccessPoint will not respond to frames where the source mac-address is unrecognized.
Station->AccessPoint payload data, relative to frame_end above (frame size depends on whether the challenge is enabled):


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,343: Line 1,704:
! Description
! Description
|-
|-
| 0x38 || 0x10 || The Station sets this to random data. Unused by the AccessPoint, except for copying into the response.
| 0x0 || 0x20 || [[#UserConfig]]+0. Copied into state by the AccessPoint.
|-
|-
| 0x48 || 0x20 || [[#UserConfig]]+0. Copied into state by the AccessPoint.
| 0x20 || 0x2 || Big-endian LocalCommunicationVersion. Byte-swapped by the AccessPoint then copied into state. [?+] This is now ignored.
|-
|-
| 0x68 || 0x2 || Big-endian LocalCommunicationVersion. Byte-swapped by the AccessPoint then copied into state.
| 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.
|-
|-
| 0x6A || 0x1E || Zeros, unused by the AccessPoint.
| 0x23 || 0x1D || Zeros, unused by the AccessPoint.
|-
|-
| 0x88 || 0x24 || [6.0.0+] Zeros, unused by the AccessPoint.
| 0x40 || 0x24 || [6.0.0+] Zeros, unused by the AccessPoint.
|-
|-
| 0xAC || 0x300 || [6.0.0+] Authentication challenge data. If enabled, the total frame size must be >= {end offset of this data in the frame}. The frame data does not include this if it's not enabled.
| 0x64 || 0x300 || [6.0.0+] Authentication challenge data. If enabled, the total frame size must be >= {end offset of this data in the frame}. The frame data does not include this if it's not enabled.
|}
|}


AccessPoint->Station response frame, relative to +0x0 above (frame size depends on whether +0x48/+0xCC are enabled):
AccessPoint->Station response payload data, relative to frame_end above (frame size depends on whether the challenge is enabled):


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,364: Line 1,725:
! Description
! Description
|-
|-
| 0x38 || 0x10 || +0x38 from the data originally sent by the Station. The Station verifies that this matches the previously sent data.
| 0x0 || 0x1 ||  
|-
|-
| 0x48 || 0x40 || Zeros. [6.0.0+] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
| 0x1 || 0x3F || Zeros. [6.0.0-?] Only included in the frame if it's enabled (+0x0 [[#AuthVersion]] >= 3). Unused by the Station.
|-
|-
| 0x88 || 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.
|-
|-
| 0xCC || 0x100 || [6.0.0+] If enabled, Authentication challenge response data. Not included in the frame if it's not enabled.
| 0x84 || 0x100 || [6.0.0+] If enabled, Authentication challenge response data. Not included in the frame if it's not enabled.
|}
|}


===== AuthVersion =====
===== AuthVersion =====
Must be 0x1-0xF.
Must be 0x1-0xF (<x010 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.


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,385: Line 1,748:
|-
|-
| 3 || [6.0.0+]
| 3 || [6.0.0+]
|-
| 4 || [19.0.0+]
|}
|}


Line 1,421: Line 1,786:
| 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 = encrypted, {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, must be <=0x500, and must match {total frame size relative to +0x0 above} + 0x48.
| 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}.
|-
|-
| 0x24 || 0x4 || Big-endian u32 Counter. The initial value is randomly-generated. This is incremented each time the below content is updated (including initial creation). Also used by the Station to determine whether the frame changed compared to a previous one. When comparing against a previous frame, new_counter-prev_counter must be <= 0xFF, and the counters must not match.
| 0x24 || 0x4 || Big-endian u32 Counter. The initial value is randomly-generated. This is incremented each time the below content is updated (including initial creation). Also used by the Station to determine whether the frame changed compared to a previous one. When comparing against a previous frame, new_counter-prev_counter must be <= 0xFF, and the counters must not match.
|-
|-
| 0x28 || 0x20 || SHA256 hash over the entire frame starting at +0x0, with the above size + 0x48. During hashing, this hash is cleared, with the new hash overwriting the original in memory (the original is copied to stack for comparing).
| 0x28 || 0x20 || EncryptionType1-2: SHA256 hash over the entire frame starting at +0x0, with the above size + 0x48. During hashing, this hash is cleared, with the new hash overwriting the original in memory (the original is copied to stack for comparing).
|-
| 0x28 || 0x10 || EncryptionType3: AES-128-GCM MAC tag (replaces the SHA256 hash).
|}
|}


When encryption is enabled, the encrypted data is +0x28 with size {remaining frame size}. This is encrypted with AES-128-CTR. The key is derived from the raw 0x20-bytes at +0x0. The CTR is {raw Counter above without byte-swap}, with the rest cleared to zeros.
Using EncryptionType3 outside of [[#Scan]]/[[#ScanPrivate]] is enabled with [[#Protocol|Protocol]] 3.


The content data at +0x48 follows, which has the size specified above (which must be >=0x500), where all fields are big-endian:
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:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,446: Line 1,817:
| 0x12 || 0x1 || [[#NetworkInfo]]+0x62
| 0x12 || 0x1 || [[#NetworkInfo]]+0x62
|-
|-
| 0x13 || 0x3 || Unused
| 0x13 || 0x1 || Unused
|-
| 0x14 || 0x2 ||
|-
|-
| 0x16 || 0x1 || s8 [[#NetworkInfo]]+0x66, clamped to range 1-8.
| 0x16 || 0x1 || s8 [[#NetworkInfo]]+0x66, clamped to range 1-8.
Line 1,463: Line 1,836:
|-
|-
| 0x4F8 || 0x8 || [6.0.0+] [[#NetworkInfo]]+0x478
| 0x4F8 || 0x8 || [6.0.0+] [[#NetworkInfo]]+0x478
|}
For EncryptionType3:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x10 || [[#NetworkInfo]]+0x50
|-
| 0x10 || 0x8 || [[#NetworkInfo]]+0x478
|-
| 0x18 || 0x1 || [[#NetworkInfo]]+0x60
|-
| 0x19 || 0x1 || [[#NetworkInfo]]+0x62
|-
| 0x1A || 0x2 || s16 LocalCommunicationVersion. Must not be negative.
|-
| 0x1C || 0x8 || Unused?
|-
| 0x24 || 0x2 || Same as +0x14 in the above struct for EncryptionType1-2.
|-
| 0x26 || 0x1 || s8 [[#NetworkInfo]]+0x66 (NodeCountMax)
|-
| 0x27 || 0x1 || s8 [[#NetworkInfo]]+0x67 (NodeCount)
|-
| 0x28 || NodeCount*0x30 || Array of the below node struct.
|-
| 0x28 + (NodeCount*0x30) || 0x2 || [[#NetworkInfo]]+0x26A (AdvertiseDataSize)
|-
| 0x2A + (NodeCount*0x30) || AdvertiseDataSize || [[#NetworkInfo]]+0x26C (AdvertiseData)
|}
|}


The data here is copied into [[#NetworkInfo]].
The data here is copied into [[#NetworkInfo]].


Node data used in the above array (all fields big-endian), which are copied into the [[#NetworkInfo]] [[#NodeInfo|NodeInfo]] array:
Node data used in the above array (all fields big-endian), which are copied into the [[#NetworkInfo]] [[#NodeInfo|NodeInfo]] array. For EncryptionType1-2:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,481: Line 1,887:
| 0xA || 0x1 || bool IsConnected
| 0xA || 0x1 || bool IsConnected
|-
|-
| 0xB || 0x1 || Unused
| 0xB || 0x1 || [19.0.0+] [[#NodeInfo|NodeInfo]] +0x2D
|-
|-
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
Line 1,489: Line 1,895:
| 0x2E || 0xA || Unused
| 0x2E || 0xA || Unused
|}
|}
For EncryptionType3:
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0 || 0x4 || [[#Ipv4Address]]
|-
| 0x4 || 0x6 || [[#MacAddress|MacAddress]]
|-
| 0xA || 0x1 || NodeId
|-
| 0xB || 0x1 || [[#NodeInfo|NodeInfo]] +0x2D
|-
| 0xC || 0x20 || First 0x20-bytes of [[#UserConfig]].
|-
| 0x2C || 0x4 || Unused
|}
=== ActionFrame2 ===
The Action frames used by [[#SendActionFrame|SendActionFrame]]/[[#RecvActionFrame|RecvActionFrame]] have the following structure:
* "Fixed parameters":
** "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 ==
== lp2p ==
This is used for communicating with accessories over local wifi - in particular, [[Mario Kart Live: Home Circuit]] uses this.
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.
A beacon is broadcasted.
Line 1,499: Line 1,979:
Communication uses sockets with standard Data frames and the above Action frames. Switch consoles presumably only use the Action frames to communicate with each other?
Communication uses sockets with standard Data frames and the above Action frames. Switch consoles presumably only use the Action frames to communicate with each other?


This uses infrastructure-mode (AccessPoint), and DHCP is used. The group-owner is the AccessPoint. Note that the probe response includes the same Nintendo tags included with the beacon. Once connected, the group-owner sends the same Epigram-vendor Action frame(s) described in [[#ldn]].
Key A derived by ldn-sysmodule is used directly as the static CCMP key for all data-frames (CCMP / MIC is standard). However, with [[#GroupInfo]]+0x8A value 3, standard WPA2-PSK is used instead.


It's unknown whether any custom authentication is used.
This uses infrastructure-mode (AccessPoint), and DHCP is used. The group-owner is the AccessPoint. Note that the probe response includes the same Nintendo tags included with the beacon. Once connected, the group-owner sends the same Epigram-vendor Action frame(s) described in [[#ldn]]. At this point socket communication can begin, including DHCP usage.


The DHCP server thread is started by the "nn.lp2p.StateMachine" thread eventually during group [[#CreateGroup|creation]]. The DHCP Offer option values are the following, at least when connecting with encryption disabled from a Linux machine:
The DHCP server thread is started by the "nn.lp2p.StateMachine" thread eventually during group [[#CreateGroup|creation]]. The DHCP Offer option values are the following:
* "Subnet Mask: 255.255.255.0"
* "Subnet Mask: 255.255.255.0"
* "DHCP Server Identifier: {...}"
* "DHCP Server Identifier: {...}"
Line 1,515: Line 1,995:


=== Beacon ===
=== Beacon ===
The SSID in the beacon is hidden (all-zero with the same length as the original SSID). The beacon contains two custom Nintendo tags with OUI <code>00:22:aa</code>. The beacon is identical to ldn, except for the following (besides SSID length difference and the lp2p-only Nintendo tags):  
The SSID in the beacon can optionally be [[#GroupInfo|hidden]] (all-zero with the same length as the original SSID). The beacon contains two vendor-specific Nintendo information elements with OUI <code>00:22:aa</code>; each IE has a 2-byte ID following the OUI. These Nintendo IEs are not used when standard WPA2-PSK is being used.
* "Tag: Traffic Indication Map (TIM)": "DTIM count" for lp2p is 1, with ldn it's 0.
 
The beacon is identical to ldn, except for the following (besides SSID length difference and the lp2p-only Nintendo tags):  
* "Tag: HT Capabilities (802.11n D1.10)": "HT Short GI for 20MHz" is set to "Not supported", for ldn it's "Supported".
* "Tag: HT Capabilities (802.11n D1.10)": "HT Short GI for 20MHz" is set to "Not supported", for ldn it's "Supported".
* "Tag: Vendor Specific: Microsoft Corp.: WMM/WME: Parameter Element" "Ac Parameters ACI 0": "CW Min: 15" for lp2p, "CW Min: 63" for ldn.
* "Tag: Vendor Specific: Microsoft Corp.: WMM/WME: Parameter Element" "Ac Parameters ACI 0": "CW Min: 15" for lp2p, "CW Min: 63" for ldn.


Note that during group creation the beacon may be missing the Nintendo tags in some cases, since group creation didn't finish yet.
Note that during group creation the beacon may be missing the Nintendo IEs in some cases, since group creation didn't finish yet.
 
==== Nintendo IE 0 ====


The first Nintendo tag contains the following data:
The first Nintendo IE (ID 0x0600) contains the following fixed parameters:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,530: Line 2,013:
! Description
! Description
|-
|-
| 0x0 || 0x2 || Usually 06 00?
| 0x0 || 0x1 || Fixed 0x20; perhaps a version or other magic number.
|-
| 0x1 || 0x1 || [[#GroupInfo|SecurityType]]
|-
|-
| 0x2 || 0x2 || Usually 20 02?(Second byte depends on whether encryption is used?)
| 0x2 || 0x1 || [[#GroupInfo|StaticAesKeyIndex]]
|-
|-
| 0x4 || 0x2 || Usually 01 00 or 02 00?(varies)
| 0x3 || 0x1 || Fixed zero; padding byte.
|-
|-
| 0x6 || 0x8 || Big-endian version of [[#GroupInfo]]+0x10.
| 0x4 || 0x8 || Big-endian (i.e. byte-reversed) version of [[#GroupInfo|LocalCommunicationId]]. This is the only context where LocalCommunicationId is reversed.
|-
|-
| 0xE || 0x10 || Same as [[#GroupInfo]]+0x0.
| 0xC || 0x10 || Wrapped master key. Same as [[#GroupInfo]]+0x0.
|-
|-
| 0x1E || || Start of encrypted (and crypto-related) data if enabled. 0x22-bytes when encrypted, 0xE-bytes when plaintext.
| 0x1C || 0x4 || If encryption is enabled, a randomly-generated nonce, else nothing. Appending 8 zero bytes to this yields the AES-GCM IV.
|-
| 0x20 || 0x10 || If encryption is enabled, the AES-GCM MAC tag, else nothing. All bytes prior to this (fixed 0x20 through nonce) are the additional authenticated data. All bytes after this are encrypted with key B.
|}
|}


The second Nintendo tag contains the following data (data starting at +0x2 is encrypted if enabled, there's also an additional 0x14-bytes when encrypted):
After this, TLV tagged parameters occur. Each TLV tag is formatted as:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 1,551: Line 2,038:
! Description
! Description
|-
|-
| 0x0 || 0x2 || Usually 06 01?
| 0x0 || 0x1 || Tag type
|-
| 0x1 || 0x1 || Length
|-
| 0x2 || {above size} || Data for the tag
|}
 
Known TLV tags:
 
{| class="wikitable" border="1"
|-
! Type
! Size
! Description
|-
| 0x1 || 0x2 || Additional network parameters: 0xAB 0xCD. A=[[#GroupInfo]]+0x82, B=[[#GroupInfo|MemberCountMax]], C=[[#GroupInfo|NetworkMode]], D=[[#GroupInfo|PerformanceRequirement]].
|-
|-
| 0x2 || 0x1 || Plaintext: usually 21?
| 0x2 || 0x8 || Flags: Bitwise-or of (1<<f) for each entry in [[#GroupInfo]]+0x40
|}
 
==== Nintendo IE 1 ====
 
The second Nintendo IE (ID 0x0601) contains only TLVs. If encryption is enabled, a 0x4-byte nonce and 0x10-byte AES-GCM tag are written first, as above, and the TLVs are encrypted. Key C is used.
 
Known TLV tags:
 
{| class="wikitable" border="1"
|-
|-
| 0x3 || 0x1 || Plaintext: AdvertiseData size
! Type
! Size
! Description
|-
|-
| 0x4 || {above size} || Plaintext: AdvertiseData
| 0x21 || Varies || AdvertiseData
|}
|}