diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 2271bea031e3ff..04765b096a8a8c 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -297,6 +297,14 @@ CHIP_ERROR DeviceController::GetPeerAddressAndPort(PeerId peerId, Inet::IPAddres return CHIP_NO_ERROR; } +CHIP_ERROR DeviceController::GetPeerAddress(NodeId nodeId, Transport::PeerAddress & addr) +{ + VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); + ReturnErrorOnFailure(mSystemState->CASESessionMgr()->GetPeerAddress( + PeerId().SetCompressedFabricId(GetCompressedFabricId()).SetNodeId(nodeId), addr)); + return CHIP_NO_ERROR; +} + CHIP_ERROR DeviceController::ComputePASEVerifier(uint32_t iterations, uint32_t setupPincode, const ByteSpan & salt, Spake2pVerifier & outVerifier) { diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index efa61e5170daf8..5e38c42905457a 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -63,8 +64,6 @@ #include #include -#include - #if CONFIG_DEVICE_LAYER #include #endif @@ -167,6 +166,17 @@ class DLL_EXPORT DeviceController : public AbstractDnssdDiscoveryController CHIP_ERROR GetPeerAddressAndPort(PeerId peerId, Inet::IPAddress & addr, uint16_t & port); + /** + * @brief + * Looks up the PeerAddress for an established CASE session. + * + * @param[in] nodeId the PeerId of the session to be found + * @param[out] addr the PeerAddress to be filled on success + * + * @return CHIP_ERROR CHIP_ERROR_NOT_CONNECTED if no CASE session exists for the device + */ + CHIP_ERROR GetPeerAddress(NodeId nodeId, Transport::PeerAddress & addr); + /** * This function finds the device corresponding to deviceId, and establishes * a CASE session with it. diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index 34a7a117475ab3..da01467c58b933 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -71,7 +71,7 @@ using namespace chip::Credentials; static void * IOThreadMain(void * arg); static CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jbyteArray pakeVerifier, jobject & outParams); -static CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobject & outLocation); +static CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jint interfaceIndex, jobject & outLocation); static CHIP_ERROR GetChipPathIdValue(jobject chipPathId, uint32_t wildcardValue, uint32_t & outValue); static CHIP_ERROR ParseAttributePathList(jobject attributePathList, std::vector & outAttributePathParamsList); @@ -268,16 +268,13 @@ JNI_METHOD(void, pairDeviceWithAddress) ChipLogProgress(Controller, "pairDeviceWithAddress() called"); - Inet::IPAddress addr; JniUtfString addrJniString(env, address); - VerifyOrReturn(Inet::IPAddress::FromString(addrJniString.c_str(), addr), - ChipLogError(Controller, "Failed to parse IP address."), - JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT)); - RendezvousParameters rendezvousParams = RendezvousParameters() - .SetDiscriminator(discriminator) - .SetSetupPINCode(pinCode) - .SetPeerAddress(Transport::PeerAddress::UDP(addr, port)); + RendezvousParameters rendezvousParams = + RendezvousParameters() + .SetDiscriminator(discriminator) + .SetSetupPINCode(pinCode) + .SetPeerAddress(Transport::PeerAddress::UDP(const_cast(addrJniString.c_str()), port)); CommissioningParameters commissioningParams = CommissioningParameters(); if (csrNonce != nullptr) { @@ -523,26 +520,21 @@ JNI_METHOD(jobject, getNetworkLocation)(JNIEnv * env, jobject self, jlong handle chip::DeviceLayer::StackLock lock; AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); - chip::Inet::IPAddress addr; - uint16_t port; + Transport::PeerAddress addr; jobject networkLocation; char addrStr[50]; - CHIP_ERROR err = - wrapper->Controller()->GetPeerAddressAndPort(PeerId() - .SetCompressedFabricId(wrapper->Controller()->GetCompressedFabricId()) - .SetNodeId(static_cast(deviceId)), - addr, port); - + CHIP_ERROR err = wrapper->Controller()->GetPeerAddress(static_cast(deviceId), addr); if (err != CHIP_NO_ERROR) { ChipLogError(Controller, "Failed to get device address."); JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); } - addr.ToString(addrStr); + addr.GetIPAddress().ToString(addrStr); - err = N2J_NetworkLocation(env, env->NewStringUTF(addrStr), static_cast(port), networkLocation); + err = N2J_NetworkLocation(env, env->NewStringUTF(addrStr), static_cast(addr.GetPort()), + static_cast(addr.GetInterface().GetPlatformInterface()), networkLocation); if (err != CHIP_NO_ERROR) { ChipLogError(Controller, "Failed to create NetworkLocation"); @@ -1059,7 +1051,7 @@ CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jbyteArray p return err; } -CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobject & outLocation) +CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jint interfaceIndex, jobject & outLocation) { CHIP_ERROR err = CHIP_NO_ERROR; jmethodID constructor; @@ -1070,10 +1062,10 @@ CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobje SuccessOrExit(err); env->ExceptionClear(); - constructor = env->GetMethodID(locationClass, "", "(Ljava/lang/String;I)V"); + constructor = env->GetMethodID(locationClass, "", "(Ljava/lang/String;II)V"); VerifyOrExit(constructor != nullptr, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND); - outLocation = (jobject) env->NewObject(locationClass, constructor, ipAddress, port); + outLocation = (jobject) env->NewObject(locationClass, constructor, ipAddress, port, interfaceIndex); VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); exit: diff --git a/src/controller/java/src/chip/devicecontroller/NetworkLocation.java b/src/controller/java/src/chip/devicecontroller/NetworkLocation.java index 2f1380f98d8e96..a09aec2df50724 100644 --- a/src/controller/java/src/chip/devicecontroller/NetworkLocation.java +++ b/src/controller/java/src/chip/devicecontroller/NetworkLocation.java @@ -6,10 +6,12 @@ public final class NetworkLocation { private final String ipAddress; private final int port; + private final int interfaceIndex; - public NetworkLocation(String ipAddress, int port) { + public NetworkLocation(String ipAddress, int port, int interfaceIndex) { this.ipAddress = ipAddress; this.port = port; + this.interfaceIndex = interfaceIndex; } /** Returns the IP address (e.g. fe80::3e61:5ff:fe0c:89f8). */ @@ -21,8 +23,18 @@ public int getPort() { return port; } + /** Returns the index of the network interface to which this address belongs, or zero. */ + public int getInterfaceIndex() { + return interfaceIndex; + } + @Override public String toString() { - return String.format(Locale.ROOT, "%s[%d]", ipAddress, port); + return String.format( + Locale.ROOT, + "%s%s[%d]", + ipAddress, + (interfaceIndex == 0 ? "" : "%" + interfaceIndex), + port); } } diff --git a/src/inet/IPAddress-StringFuncts.cpp b/src/inet/IPAddress-StringFuncts.cpp index 7df8a476e60e88..1f15e05844b0fe 100644 --- a/src/inet/IPAddress-StringFuncts.cpp +++ b/src/inet/IPAddress-StringFuncts.cpp @@ -28,6 +28,7 @@ #endif #include #include +#include #include #include @@ -138,5 +139,32 @@ bool IPAddress::FromString(const char * str, size_t strLen, IPAddress & output) return res; } +bool IPAddress::FromString(const char * str, IPAddress & addrOutput, class InterfaceId & ifaceOutput) +{ + char * addrStr = const_cast(str); + char * addrPart = nullptr; + char * scopePart = nullptr; + char * strtokContext = nullptr; + + addrPart = strtok_r(addrStr, "%", &strtokContext); + if (addrPart != nullptr) + { + scopePart = strtok_r(nullptr, "%", &strtokContext); + } + + if (addrPart == nullptr || scopePart == nullptr) + { + ifaceOutput = Inet::InterfaceId(); + return Inet::IPAddress::FromString(addrStr, addrOutput); + } + + CHIP_ERROR err = Inet::InterfaceId::InterfaceNameToId(scopePart, ifaceOutput); + if (err != CHIP_NO_ERROR) + { + return false; + } + return Inet::IPAddress::FromString(addrPart, addrOutput); +} + } // namespace Inet } // namespace chip diff --git a/src/inet/IPAddress.h b/src/inet/IPAddress.h index 709b3933765ef1..0e89afa77ca347 100644 --- a/src/inet/IPAddress.h +++ b/src/inet/IPAddress.h @@ -69,6 +69,8 @@ #error Forbidden : native Open Thread implementation with IPV4 enabled #endif +#include + #define NL_INET_IPV6_ADDR_LEN_IN_BYTES (16) #define NL_INET_IPV6_MCAST_GROUP_LEN_IN_BYTES (14) @@ -386,6 +388,21 @@ class DLL_EXPORT IPAddress */ static bool FromString(const char * str, size_t strLen, IPAddress & output); + /** + * @brief + * Scan the IP address from its conventional presentation text, including + * the interface ID if present. (e.g. "fe80::2%wlan0"). If no interface ID + * is present, then ifaceOutput will be set to the null interface ID. + * + * @param[in] str A pointer to the text to be scanned. + * @param[out] addrOutput The object to set to the IP address. + * @param[out] ifaceOutput The object to set to the interface ID. + * + * @retval true The presentation format is valid + * @retval false Otherwise + */ + static bool FromString(const char * str, IPAddress & addrOutput, class InterfaceId & ifaceOutput); + /** * @brief Emit the IP address in standard network representation. * diff --git a/src/inet/InetInterface.cpp b/src/inet/InetInterface.cpp index 688935b02cf5c1..f0a6310877d8cb 100644 --- a/src/inet/InetInterface.cpp +++ b/src/inet/InetInterface.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT #include @@ -435,6 +436,21 @@ CHIP_ERROR InterfaceId::GetInterfaceName(char * nameBuf, size_t nameBufSize) con CHIP_ERROR InterfaceId::InterfaceNameToId(const char * intfName, InterfaceId & interface) { + // First attempt to parse as a numeric ID: + char * parseEnd; + unsigned long intfNum = strtoul(intfName, &parseEnd, 10); + if (*parseEnd == 0) + { + if (intfNum > 0 && intfNum < UINT8_MAX && CanCastTo(intfNum)) + { + interface = InterfaceId(static_cast(intfNum)); + return CHIP_NO_ERROR; + } + + return INET_ERROR_UNKNOWN_INTERFACE; + } + + // Falling back to name -> ID lookup otherwise (e.g. wlan0) unsigned int intfId = if_nametoindex(intfName); interface = InterfaceId(intfId); if (intfId == 0) diff --git a/src/platform/android/DnssdImpl.cpp b/src/platform/android/DnssdImpl.cpp index 244032497456dc..d536fdd2f89999 100644 --- a/src/platform/android/DnssdImpl.cpp +++ b/src/platform/android/DnssdImpl.cpp @@ -290,17 +290,20 @@ void HandleResolve(jstring instanceName, jstring serviceType, jstring hostName, JniUtfString jnihostName(env, hostName); JniUtfString jniAddress(env, address); Inet::IPAddress ipAddress; + Inet::InterfaceId iface; VerifyOrReturn(strlen(jniInstanceName.c_str()) <= Operational::kInstanceNameMaxLength, dispatch(CHIP_ERROR_INVALID_ARGUMENT)); VerifyOrReturn(strlen(jniServiceType.c_str()) <= kDnssdTypeAndProtocolMaxSize, dispatch(CHIP_ERROR_INVALID_ARGUMENT)); VerifyOrReturn(CanCastTo(port), dispatch(CHIP_ERROR_INVALID_ARGUMENT)); - VerifyOrReturn(Inet::IPAddress::FromString(jniAddress.c_str(), ipAddress), dispatch(CHIP_ERROR_INVALID_ARGUMENT)); + VerifyOrReturn(Inet::IPAddress::FromString(const_cast(jniAddress.c_str()), ipAddress, iface), + dispatch(CHIP_ERROR_INVALID_ARGUMENT)); DnssdService service = {}; CopyString(service.mName, jniInstanceName.c_str()); CopyString(service.mHostName, jnihostName.c_str()); CopyString(service.mType, jniServiceType.c_str()); service.mPort = static_cast(port); + service.mInterface = iface; service.mTextEntrySize = 0; service.mTextEntries = nullptr; diff --git a/src/transport/raw/PeerAddress.h b/src/transport/raw/PeerAddress.h index 32e6d53ed34bf9..d97398bc79566f 100644 --- a/src/transport/raw/PeerAddress.h +++ b/src/transport/raw/PeerAddress.h @@ -23,8 +23,6 @@ #pragma once -#include - #include #include #include @@ -186,12 +184,26 @@ class PeerAddress static PeerAddress BLE() { return PeerAddress(Type::kBle); } static PeerAddress UDP(const Inet::IPAddress & addr) { return PeerAddress(addr, Type::kUdp); } static PeerAddress UDP(const Inet::IPAddress & addr, uint16_t port) { return UDP(addr).SetPort(port); } + + /** + * Parses a PeerAddress from the given IP address string with UDP type. For example, + * "192.168.1.4", "fe80::2", "fe80::1%wlan0". Notably this will also include the network scope + * ID in either index or name form (e.g. %wlan0, %14). + */ + static PeerAddress UDP(char * addrStr, uint16_t port) { return PeerAddress::FromString(addrStr, port, Type::kUdp); } static PeerAddress UDP(const Inet::IPAddress & addr, uint16_t port, Inet::InterfaceId interface) { return UDP(addr).SetPort(port).SetInterface(interface); } static PeerAddress TCP(const Inet::IPAddress & addr) { return PeerAddress(addr, Type::kTcp); } static PeerAddress TCP(const Inet::IPAddress & addr, uint16_t port) { return TCP(addr).SetPort(port); } + + /** + * Parses a PeerAddress from the given IP address string with TCP type. For example, + * "192.168.1.4", "fe80::2", "fe80::1%wlan0". Notably this will also include the network scope + * ID in either index or name form (e.g. %wlan0, %14). + */ + static PeerAddress TCP(char * addrStr, uint16_t port) { return PeerAddress::FromString(addrStr, port, Type::kTcp); } static PeerAddress TCP(const Inet::IPAddress & addr, uint16_t port, Inet::InterfaceId interface) { return TCP(addr).SetPort(port).SetInterface(interface); @@ -214,6 +226,13 @@ class PeerAddress } private: + static PeerAddress FromString(char * addrStr, uint16_t port, Type type) + { + Inet::IPAddress addr; + Inet::InterfaceId interfaceId; + Inet::IPAddress::FromString(addrStr, addr, interfaceId); + return PeerAddress(addr, type).SetPort(port).SetInterface(interfaceId); + } Inet::IPAddress mIPAddress = {}; Type mTransportType = Type::kUndefined; uint16_t mPort = CHIP_PORT; ///< Relevant for UDP data sending.