Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for commisionable advertisement #4337

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 43 additions & 8 deletions examples/minimal-mdns/advertiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,24 @@ enum class AdvertisingMode
{
kCommisioning,
kOperational,
kCommisionable,
};

struct Options
{
bool enableIpV4 = false;
AdvertisingMode advertisingMode = AdvertisingMode::kCommisioning;

// commisioning params
// commisioning/commisionable params
uint8_t shortDiscriminator = 52;
uint16_t longDiscriminator = 840;
Optional<uint16_t> vendorId;
Optional<uint16_t> productId;

// commisionable params
Optional<const char *> pairingInstr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required for commisionable as well? if not, I think we should add a comment that these 2 are for commisionable only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only required for commisionable. Will add a comment.

Optional<uint8_t> pairingHint;

// operational params
uint64_t fabricId = 12345;
uint64_t nodeId = 6789;
Expand All @@ -61,6 +66,8 @@ constexpr uint16_t kOptionCommisioningShordDiscriminator = 's';
constexpr uint16_t kOptionCommisioningLongDiscriminaotr = 'l';
constexpr uint16_t kOptionCommisioningVendorId = 0x100; // v is used by 'version'
constexpr uint16_t kOptionCommisioningProductId = 'p';
constexpr uint16_t kOptionCommisioningPairingInstr = 0x200; // Just use the long format
constexpr uint16_t kOptionCommisioningPairingHint = 0x300;

constexpr uint16_t kOptionOperationalFabricId = 'f';
constexpr uint16_t kOptionOperationalNodeId = 'n';
Expand All @@ -73,14 +80,18 @@ bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier,
gOptions.enableIpV4 = true;
return true;
case kOptionAdvertisingMode:
if ((strcmp(aValue, "operational") == 0) || (strcmp(aValue, "o") == 0))
if (strcmp(aValue, "operational") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kOperational;
}
else if ((strcmp(aValue, "commisioning") == 0) || (strcmp(aValue, "c") == 0))
else if (strcmp(aValue, "commisioning") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kCommisioning;
}
else if (strcmp(aValue, "commisionable") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kCommisionable;
}
else
{
PrintArgError("%s: Invalid advertising mode %s\n", aProgram, aValue);
Expand All @@ -100,6 +111,12 @@ bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier,
case kOptionCommisioningProductId:
gOptions.productId = Optional<uint16_t>::Value(static_cast<uint16_t>(atoi(aValue)));
return true;
case kOptionCommisioningPairingInstr:
gOptions.pairingInstr = Optional<const char *>::Value(static_cast<const char *>(aValue));
return true;
case kOptionCommisioningPairingHint:
gOptions.pairingHint = Optional<uint8_t>::Value(static_cast<uint8_t>(atoi(aValue)));
return true;
case kOptionOperationalFabricId:
gOptions.fabricId = atoll(aValue);
return true;
Expand All @@ -122,6 +139,8 @@ OptionDef cmdLineOptionsDef[] = {
{ "long-discriminator", kArgumentRequired, kOptionCommisioningLongDiscriminaotr },
{ "vendor-id", kArgumentRequired, kOptionCommisioningVendorId },
{ "product-id", kArgumentRequired, kOptionCommisioningProductId },
{ "pairing-instruction", kArgumentRequired, kOptionCommisioningPairingInstr },
{ "pairing-hint", kArgumentRequired, kOptionCommisioningPairingHint },

{ "fabrid-id", kArgumentRequired, kOptionOperationalFabricId },
{ "node-id", kArgumentRequired, kOptionOperationalNodeId },
Expand All @@ -136,18 +155,22 @@ OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS"
#endif
" -m <mode>\n"
" --advertising-mode <mode>\n"
" Advertise in this mode (o/operational or c/commisioning).\n"
" Advertise in this mode (operational or commisioning or commisionable).\n"
" --short-discriminator <value>\n"
" -s <value>\n"
" Commisioning short discriminator.\n"
" Commisioning/commisionable short discriminator.\n"
" --long-discriminator <value>\n"
" -l <value>\n"
" Commisioning long discriminator.\n"
" Commisioning/commisionable long discriminator.\n"
" --vendor-id <value>\n"
" Commisioning vendor id.\n"
" Commisioning/commisionable vendor id.\n"
" --product-id <value>\n"
" -p <value>\n"
" Commisioning product id.\n"
" Commisioning/commisionable product id.\n"
" --pairing-instruction <value>\n"
" Commisionable pairing instruction.\n"
" --pairing-hint <value>\n"
" Commisionable pairing hint.\n"
" --fabrid-id <value>\n"
" -f <value>\n"
" Operational fabric id.\n"
Expand Down Expand Up @@ -207,6 +230,18 @@ int main(int argc, char ** args)
.SetFabricId(gOptions.fabricId)
.SetNodeId(gOptions.nodeId));
}
else if (gOptions.advertisingMode == AdvertisingMode::kCommisionable)
{
err = chip::Mdns::ServiceAdvertiser::Instance().Advertise(chip::Mdns::CommisionableAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT)
.SetShortDiscriminator(gOptions.shortDiscriminator)
.SetLongDiscrimininator(gOptions.longDiscriminator)
.SetVendorId(gOptions.vendorId)
.SetProductId(gOptions.productId)
.SetPairingInstr(gOptions.pairingInstr)
.SetPairingHint(gOptions.pairingHint));
}
else
{
fprintf(stderr, "FAILED to determine advertising type.\n");
Expand Down
57 changes: 57 additions & 0 deletions src/lib/mdns/Advertiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,60 @@ class CommisioningAdvertisingParameters : public BaseAdvertisingParams<Commision
chip::Optional<uint16_t> mProductId;
};

class CommisionableAdvertisingParameters : public BaseAdvertisingParams<CommisionableAdvertisingParameters>
{
public:
CommisionableAdvertisingParameters & SetShortDiscriminator(uint8_t discriminator)
{
mShortDiscriminator = discriminator;
return *this;
}
uint8_t GetShortDiscriminator() const { return mShortDiscriminator; }

CommisionableAdvertisingParameters & SetLongDiscrimininator(uint16_t discriminator)
{
mLongDiscriminator = discriminator;
return *this;
}
uint16_t GetLongDiscriminator() const { return mLongDiscriminator; }

CommisionableAdvertisingParameters & SetVendorId(Optional<uint16_t> vendorId)
{
mVendorId = vendorId;
return *this;
}
Optional<uint16_t> GetVendorId() const { return mVendorId; }

CommisionableAdvertisingParameters & SetProductId(Optional<uint16_t> productId)
{
mProductId = productId;
return *this;
}
Optional<uint16_t> GetProductId() const { return mProductId; }

CommisionableAdvertisingParameters & SetPairingInstr(Optional<const char *> pairingInstr)
{
mPairingInstr = pairingInstr;
return *this;
}
Optional<const char *> GetPairingInstr() const { return mPairingInstr; }

CommisionableAdvertisingParameters & SetPairingHint(Optional<uint8_t> pairingHint)
{
mPairingHint = pairingHint;
return *this;
}
Optional<uint8_t> GetPairingHint() const { return mPairingHint; }

private:
uint8_t mShortDiscriminator = 0;
uint16_t mLongDiscriminator = 0; // 12-bit according to spec
chip::Optional<uint16_t> mVendorId;
chip::Optional<uint16_t> mProductId;
chip::Optional<const char *> mPairingInstr;
chip::Optional<uint8_t> mPairingHint;
};

/// Handles advertising of CHIP nodes
class ServiceAdvertiser
{
Expand All @@ -129,6 +183,9 @@ class ServiceAdvertiser
/// Advertises the CHIP node as a commisioning node
virtual CHIP_ERROR Advertise(const CommisioningAdvertisingParameters & params) = 0;

/// Advertises the CHIP node as a commisionable node
virtual CHIP_ERROR Advertise(const CommisionableAdvertisingParameters & params) = 0;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a method to stop this advertisement?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not currently - we assume that 'once a chip device, always a chip device and we advertise'. Having a 'StopAdvertising' seems possible though.

/// Provides the system-wide implementation of the service advertiser
static ServiceAdvertiser & Instance();
};
Expand Down
144 changes: 144 additions & 0 deletions src/lib/mdns/Advertiser_ImplMinimalMdns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class AdvertiserMinMdns : public ServiceAdvertiser,
CHIP_ERROR Start(chip::Inet::InetLayer * inetLayer, uint16_t port) override;
CHIP_ERROR Advertise(const OperationalAdvertisingParameters & params) override;
CHIP_ERROR Advertise(const CommisioningAdvertisingParameters & params) override;
CHIP_ERROR Advertise(const CommisionableAdvertisingParameters & params) override;

// ServerDelegate
void OnQuery(const BytesRange & data, const chip::Inet::IPPacketInfo * info) override;
Expand Down Expand Up @@ -266,6 +267,7 @@ class AdvertiserMinMdns : public ServiceAdvertiser,
}

FullQName GetCommisioningTextEntries(const CommisioningAdvertisingParameters & params);
FullQName GetCommisionableTextEntries(const CommisionableAdvertisingParameters & params);

static constexpr size_t kMaxEndPoints = 10;
static constexpr size_t kMaxRecords = 16;
Expand Down Expand Up @@ -559,6 +561,148 @@ FullQName AdvertiserMinMdns::GetCommisioningTextEntries(const CommisioningAdvert
return AllocateQName(txtDiscriminator, txtVidPid);
}

CHIP_ERROR AdvertiserMinMdns::Advertise(const CommisionableAdvertisingParameters & params)
{
Clear();

// TODO: need to detect colisions here
char nameBuffer[64] = "";
size_t len = snprintf(nameBuffer, sizeof(nameBuffer), "chip-%016" PRIX64, GetRandU64());
if (len >= sizeof(nameBuffer))
{
return CHIP_ERROR_NO_MEMORY;
}

FullQName operationalServiceName = AllocateQName("_chipd", "_udp", "local");
FullQName operationalServerName = AllocateQName(nameBuffer, "_chipd", "_udp", "local");
FullQName serverName = AllocateQName(nameBuffer, "local");

if ((operationalServiceName.nameCount == 0) || (operationalServerName.nameCount == 0) || (serverName.nameCount == 0))
{
ChipLogError(Discovery, "Failed to allocate QNames.");
return CHIP_ERROR_NO_MEMORY;
}

if (!AddResponder<PtrResponder>(operationalServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
{
ChipLogError(Discovery, "Failed to add service PTR record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

if (!AddResponder<SrvResponder>(SrvResourceRecord(operationalServerName, serverName, params.GetPort()))
.SetReportAdditional(serverName)
.IsValid())
{
ChipLogError(Discovery, "Failed to add SRV record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
if (!AddResponder<IPv6Responder>(serverName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv6 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

if (params.IsIPv4Enabled())
{
if (!AddResponder<IPv4Responder>(serverName).IsValid())
{
ChipLogError(Discovery, "Failed to add IPv4 mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
}

{
sprintf(nameBuffer, "S%03d", params.GetShortDiscriminator());
FullQName shortServiceName = AllocateQName(nameBuffer, "_sub", "_chipd", "_udp", "local");
ReturnErrorCodeIf(shortServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);

if (!AddResponder<PtrResponder>(shortServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
{
ChipLogError(Discovery, "Failed to add short discriminator PTR record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
}

{
sprintf(nameBuffer, "L%04d", params.GetLongDiscriminator());
FullQName longServiceName = AllocateQName(nameBuffer, "_sub", "_chipd", "_udp", "local");
ReturnErrorCodeIf(longServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);
if (!AddResponder<PtrResponder>(longServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
{
ChipLogError(Discovery, "Failed to add long discriminator PTR record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
}

if (params.GetVendorId().HasValue())
{
sprintf(nameBuffer, "V%d", params.GetVendorId().Value());
FullQName vendorServiceName = AllocateQName(nameBuffer, "_sub", "_chipd", "_udp", "local");
ReturnErrorCodeIf(vendorServiceName.nameCount == 0, CHIP_ERROR_NO_MEMORY);

if (!AddResponder<PtrResponder>(vendorServiceName, operationalServerName)
.SetReportAdditional(operationalServerName)
.SetReportInServiceListing(true)
.IsValid())
{
ChipLogError(Discovery, "Failed to add vendor discriminator PTR record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}
}

if (!AddResponder<TxtResponder>(TxtResourceRecord(operationalServerName, GetCommisionableTextEntries(params)))
.SetReportAdditional(serverName)
.IsValid())
{
ChipLogError(Discovery, "Failed to add TXT record mDNS responder");
return CHIP_ERROR_NO_MEMORY;
}

ChipLogProgress(Discovery, "CHIP minimal mDNS configured as 'Commisionable device'.");

return CHIP_NO_ERROR;
}

FullQName AdvertiserMinMdns::GetCommisionableTextEntries(const CommisionableAdvertisingParameters & params)
{
// a discriminator always exists
char txtDiscriminator[32];
sprintf(txtDiscriminator, "D=%d", params.GetLongDiscriminator());

if (!params.GetVendorId().HasValue())
{
return AllocateQName(txtDiscriminator);
}

// Need to also set a vid/pid string
char txtVidPid[64];
if (params.GetProductId().HasValue())
{
sprintf(txtVidPid, "V=%d+%d", params.GetVendorId().Value(), params.GetProductId().Value());
}
else
{
sprintf(txtVidPid, "V=%d", params.GetVendorId().Value());
}

char txtPairingInstrHint[128];
if (params.GetPairingInstr().HasValue() && params.GetPairingHint().HasValue())
{
sprintf(txtPairingInstrHint, "P=%s+%d", params.GetPairingInstr().Value(), params.GetPairingHint().Value());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to ensure that GetPairingInstr() HasValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

}

return AllocateQName(txtDiscriminator, txtVidPid, txtPairingInstrHint);
}

AdvertiserMinMdns gAdvertiser;
} // namespace

Expand Down
6 changes: 6 additions & 0 deletions src/lib/mdns/Advertiser_ImplNone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ class NoneAdvertiser : public ServiceAdvertiser
ChipLogError(Discovery, "mDNS advertising not available. Commisioning Advertisement failed.");
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR Advertise(const CommisionableAdvertisingParameters & params) override
{
ChipLogError(Discovery, "mDNS advertising not available. Commisionable Advertisement failed.");
return CHIP_ERROR_NOT_IMPLEMENTED;
}
};

NoneAdvertiser gAdvertiser;
Expand Down