Skip to content

Commit

Permalink
Add support for scope IDs for link-local addresses in Java (#19394)
Browse files Browse the repository at this point in the history
* Add support for scope IDs for link-local addresses in Java

Currently the DNS-SD resolver endpoints, and the getNetworkLocation()
and establishPaseConnection() entry points which expect an address as a
String do not support receiving an address string which includes the
scope ID. For example, fe80::2%wlan0 or fe80::1%47.

Added support to accept either form, either the index (%47) or the name
(%wlan0).

Tested internally as well as internally by commissioning with CHIPTool.

* Fix "return after else"

Co-authored-by: Andrei Litvin <andy314@gmail.com>
  • Loading branch information
2 people authored and pull[bot] committed Jul 12, 2022
1 parent 6892488 commit 3eae6ab
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 29 deletions.
8 changes: 8 additions & 0 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
14 changes: 12 additions & 2 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <credentials/FabricTable.h>
#include <credentials/attestation_verifier/DeviceAttestationDelegate.h>
#include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
#include <inet/InetInterface.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPPersistentStorageDelegate.h>
Expand All @@ -63,8 +64,6 @@
#include <transport/TransportMgr.h>
#include <transport/raw/UDP.h>

#include <controller/CHIPDeviceControllerSystemState.h>

#if CONFIG_DEVICE_LAYER
#include <platform/CHIPDeviceLayer.h>
#endif
Expand Down Expand Up @@ -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.
Expand Down
36 changes: 14 additions & 22 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<app::AttributePathParams> & outAttributePathParamsList);
Expand Down Expand Up @@ -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<char *>(addrJniString.c_str()), port));
CommissioningParameters commissioningParams = CommissioningParameters();
if (csrNonce != nullptr)
{
Expand Down Expand Up @@ -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<chip::NodeId>(deviceId)),
addr, port);

CHIP_ERROR err = wrapper->Controller()->GetPeerAddress(static_cast<chip::NodeId>(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<jint>(port), networkLocation);
err = N2J_NetworkLocation(env, env->NewStringUTF(addrStr), static_cast<jint>(addr.GetPort()),
static_cast<jint>(addr.GetInterface().GetPlatformInterface()), networkLocation);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to create NetworkLocation");
Expand Down Expand Up @@ -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;
Expand All @@ -1070,10 +1062,10 @@ CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jobje
SuccessOrExit(err);

env->ExceptionClear();
constructor = env->GetMethodID(locationClass, "<init>", "(Ljava/lang/String;I)V");
constructor = env->GetMethodID(locationClass, "<init>", "(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:
Expand Down
16 changes: 14 additions & 2 deletions src/controller/java/src/chip/devicecontroller/NetworkLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -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). */
Expand All @@ -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);
}
}
28 changes: 28 additions & 0 deletions src/inet/IPAddress-StringFuncts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#endif
#include <limits>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <inet/IPAddress.h>
Expand Down Expand Up @@ -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<char *>(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
17 changes: 17 additions & 0 deletions src/inet/IPAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
#error Forbidden : native Open Thread implementation with IPV4 enabled
#endif

#include <inet/InetInterface.h>

#define NL_INET_IPV6_ADDR_LEN_IN_BYTES (16)
#define NL_INET_IPV6_MCAST_GROUP_LEN_IN_BYTES (14)

Expand Down Expand Up @@ -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.
*
Expand Down
16 changes: 16 additions & 0 deletions src/inet/InetInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <lib/support/CHIPMemString.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/SafeInt.h>

#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
#include <lwip/netif.h>
Expand Down Expand Up @@ -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<InterfaceId::PlatformType>(intfNum))
{
interface = InterfaceId(static_cast<InterfaceId::PlatformType>(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)
Expand Down
5 changes: 4 additions & 1 deletion src/platform/android/DnssdImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint16_t>(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<char *>(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<uint16_t>(port);
service.mInterface = iface;
service.mTextEntrySize = 0;
service.mTextEntries = nullptr;

Expand Down
23 changes: 21 additions & 2 deletions src/transport/raw/PeerAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

#pragma once

#include <stdio.h>

#include <inet/IPAddress.h>
#include <inet/InetInterface.h>
#include <lib/core/CHIPConfig.h>
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand Down

0 comments on commit 3eae6ab

Please sign in to comment.