blob: 6e2602573013242501a595af268e2a25d51e1947 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstdio>
#include <memory>
#include <optional>
#include <arpa/inet.h>
#include <strings.h>
#include <TracingCommandLineArgument.h>
#include <lib/dnssd/Advertiser.h>
#include <lib/support/CHIPArgParser.hpp>
#include <lib/support/CHIPMem.h>
#include <lib/support/Span.h>
#include <platform/CHIPDeviceLayer.h>
using namespace chip;
namespace {
enum class AdvertisingMode
{
kCommissionableNode,
kOperational,
kOperationalMultiAdmin,
kCommissioner,
};
struct Options
{
bool enableIpV4 = false;
AdvertisingMode advertisingMode = AdvertisingMode::kCommissionableNode;
// commissionable node / commissioner params
std::optional<uint16_t> vendorId;
std::optional<uint16_t> productId;
std::optional<uint32_t> deviceType;
std::optional<const char *> deviceName;
// commissionable node params
uint8_t shortDiscriminator = 52;
uint16_t longDiscriminator = 840;
Dnssd::CommissioningMode commissioningMode = Dnssd::CommissioningMode::kDisabled;
std::optional<const char *> rotatingId;
std::optional<const char *> pairingInstr;
std::optional<uint16_t> pairingHint;
// operational params
uint64_t fabricId = 12345;
uint64_t nodeId = 6789;
uint8_t mac[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
} gOptions;
using namespace chip::ArgParser;
constexpr uint16_t kOptionEnableIpV4 = '4';
constexpr uint16_t kOptionAdvertisingMode = 'm';
constexpr uint16_t kOptionCommissioningShortDiscriminator = 's';
constexpr uint16_t kOptionCommissioningLongDiscriminaotr = 'l';
constexpr uint16_t kOptionCommissioningVendorId = 0x100; // v is used by 'version'
constexpr uint16_t kOptionCommissioningProductId = 'p';
constexpr uint16_t kOptionCommissioningPairingInstr = 0x200; // Just use the long format
constexpr uint16_t kOptionCommissioningPairingHint = 0x300;
constexpr uint16_t kOptionCommissioningDeviceType = 0x400;
constexpr uint16_t kOptionCommissioningDeviceName = 0x500;
constexpr uint16_t kOptionCommissioningMode = 0x600;
constexpr uint16_t kOptionCommissioningRotatingId = 0x700;
constexpr uint16_t kOptionOperationalFabricId = 'f';
constexpr uint16_t kOptionOperationalNodeId = 'n';
constexpr uint16_t kOptionTraceTo = 't';
// Only used for argument parsing. Tracing setup owned by the main loop.
chip::CommandLineApp::TracingSetup * tracing_setup_for_argparse = nullptr;
bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue)
{
uint8_t cm;
switch (aIdentifier)
{
case kOptionEnableIpV4:
gOptions.enableIpV4 = true;
return true;
case kOptionTraceTo:
tracing_setup_for_argparse->EnableTracingFor(aValue);
return true;
case kOptionAdvertisingMode:
if (strcmp(aValue, "operational") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kOperational;
}
else if (strcmp(aValue, "operational-multi-admin") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kOperationalMultiAdmin;
}
else if (strcmp(aValue, "commissionable-node") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kCommissionableNode;
}
else if (strcmp(aValue, "commissioner") == 0)
{
gOptions.advertisingMode = AdvertisingMode::kCommissioner;
}
else
{
PrintArgError("%s: Invalid advertising mode %s\n", aProgram, aValue);
return false;
}
return true;
case kOptionCommissioningShortDiscriminator:
gOptions.shortDiscriminator = static_cast<uint8_t>(atoi(aValue));
return true;
case kOptionCommissioningLongDiscriminaotr:
gOptions.longDiscriminator = static_cast<uint16_t>(atoi(aValue));
return true;
case kOptionCommissioningVendorId:
gOptions.vendorId = std::make_optional<uint16_t>(static_cast<uint16_t>(atoi(aValue)));
return true;
case kOptionCommissioningProductId:
gOptions.productId = std::make_optional<uint16_t>(static_cast<uint16_t>(atoi(aValue)));
return true;
case kOptionCommissioningMode:
cm = static_cast<uint8_t>(atoi(aValue));
if (cm == 1)
{
gOptions.commissioningMode = Dnssd::CommissioningMode::kEnabledBasic;
}
if (cm == 2)
{
gOptions.commissioningMode = Dnssd::CommissioningMode::kEnabledEnhanced;
}
return true;
case kOptionCommissioningDeviceType:
gOptions.deviceType = std::make_optional<uint32_t>(static_cast<uint32_t>(atoi(aValue)));
return true;
case kOptionCommissioningDeviceName:
gOptions.deviceName = std::make_optional<const char *>(static_cast<const char *>(aValue));
return true;
case kOptionCommissioningRotatingId:
gOptions.rotatingId = std::make_optional<const char *>(static_cast<const char *>(aValue));
return true;
case kOptionCommissioningPairingInstr:
gOptions.pairingInstr = std::make_optional<const char *>(static_cast<const char *>(aValue));
return true;
case kOptionCommissioningPairingHint:
gOptions.pairingHint = std::make_optional<uint16_t>(static_cast<uint16_t>(atoi(aValue)));
return true;
case kOptionOperationalFabricId:
if (sscanf(aValue, "%" SCNx64, &gOptions.fabricId) != 1)
{
PrintArgError("%s: Invalid fabric id: %s\n", aProgram, aValue);
return false;
}
return true;
case kOptionOperationalNodeId:
if (sscanf(aValue, "%" SCNx64, &gOptions.nodeId) != 1)
{
PrintArgError("%s: Invalid node id: %s\n", aProgram, aValue);
return false;
}
return true;
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
return false;
}
}
OptionDef cmdLineOptionsDef[] = {
#if INET_CONFIG_ENABLE_IPV4
{ "enable-ip-v4", kNoArgument, kOptionEnableIpV4 },
#endif
{ "advertising-mode", kArgumentRequired, kOptionAdvertisingMode },
{ "short-discriminator", kArgumentRequired, kOptionCommissioningShortDiscriminator },
{ "long-discriminator", kArgumentRequired, kOptionCommissioningLongDiscriminaotr },
{ "vendor-id", kArgumentRequired, kOptionCommissioningVendorId },
{ "product-id", kArgumentRequired, kOptionCommissioningProductId },
{ "commissioning-mode", kNoArgument, kOptionCommissioningMode },
{ "device-type", kArgumentRequired, kOptionCommissioningDeviceType },
{ "device-name", kArgumentRequired, kOptionCommissioningDeviceName },
{ "rotating-id", kArgumentRequired, kOptionCommissioningRotatingId },
{ "pairing-instruction", kArgumentRequired, kOptionCommissioningPairingInstr },
{ "pairing-hint", kArgumentRequired, kOptionCommissioningPairingHint },
{ "fabric-id", kArgumentRequired, kOptionOperationalFabricId },
{ "node-id", kArgumentRequired, kOptionOperationalNodeId },
{ "trace-to", kArgumentRequired, kOptionTraceTo },
{},
};
OptionSet cmdLineOptions = {
HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS",
#if INET_CONFIG_ENABLE_IPV4
" -4\n"
" --enable-ip-v4\n"
" enable listening on IPv4\n"
#endif
" -m <mode>\n"
" --advertising-mode <mode>\n"
" Advertise in this mode (operational, operational-multi-admin, commissionable-node or commissioner).\n"
" --short-discriminator <value>\n"
" -s <value>\n"
" Commissioning/commissionable short discriminator.\n"
" --long-discriminator <value>\n"
" -l <value>\n"
" Commissioning/commissionable long discriminator.\n"
" --vendor-id <value>\n"
" Commissioning/commissionable vendor id.\n"
" --product-id <value>\n"
" -p <value>\n"
" Commissioning/commissionable product id.\n"
" --commissioning-mode <value>\n"
" Commissioning Mode (0=disabled, 1=basic, 2=enhanced).\n"
" --device-type <value>\n"
" Device type id.\n"
" --device-name <value>\n"
" Name of device.\n"
" --rotating-id <value>\n"
" Rotating Id.\n"
" --pairing-instruction <value>\n"
" Commissionable pairing instruction.\n"
" --pairing-hint <value>\n"
" Commissionable pairing hint.\n"
" --fabric-id <value>\n"
" -f <value>\n"
" Operational fabric id.\n"
" --node-id <value>\n"
" -n <value>\n"
" Operational node id.\n"
" -t <dest>\n"
" --trace-to <dest>\n"
" trace to the given destination (supported: " SUPPORTED_COMMAND_LINE_TRACING_TARGETS ").\n"
"\n"
};
HelpOptions helpOptions("advertiser", "Usage: advertiser [options]", "1.0");
OptionSet * allOptions[] = { &cmdLineOptions, &helpOptions, nullptr };
void StopSignalHandler(int signal)
{
DeviceLayer::PlatformMgr().StopEventLoopTask();
}
} // namespace
int main(int argc, char ** args)
{
if (Platform::MemoryInit() != CHIP_NO_ERROR)
{
fprintf(stderr, "FAILED to initialize memory\n");
return 1;
}
if (DeviceLayer::PlatformMgr().InitChipStack() != CHIP_NO_ERROR)
{
fprintf(stderr, "FAILED to initialize chip stack\n");
return 1;
}
chip::CommandLineApp::TracingSetup tracing_setup;
tracing_setup_for_argparse = &tracing_setup;
if (!chip::ArgParser::ParseArgs(args[0], argc, args, allOptions))
{
return 1;
}
tracing_setup_for_argparse = nullptr;
if (chip::Dnssd::ServiceAdvertiser::Instance().Init(DeviceLayer::UDPEndPointManager()) != CHIP_NO_ERROR)
{
fprintf(stderr, "FAILED to start MDNS advertisement\n");
return 1;
}
CHIP_ERROR err;
if (gOptions.advertisingMode == AdvertisingMode::kCommissionableNode)
{
printf("Advertise Commissionable Node\n");
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(chip::Dnssd::CommissionAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT)
.SetShortDiscriminator(gOptions.shortDiscriminator)
.SetLongDiscriminator(gOptions.longDiscriminator)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetVendorId(gOptions.vendorId)
.SetProductId(gOptions.productId)
.SetCommissioningMode(gOptions.commissioningMode)
.SetDeviceType(gOptions.deviceType)
.SetDeviceName(gOptions.deviceName)
.SetRotatingDeviceId(gOptions.rotatingId)
.SetPairingInstruction(gOptions.pairingInstr)
.SetPairingHint(gOptions.pairingHint));
}
else if (gOptions.advertisingMode == AdvertisingMode::kOperational)
{
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(
chip::Dnssd::OperationalAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetPeerId(PeerId().SetCompressedFabricId(gOptions.fabricId).SetNodeId(gOptions.nodeId)));
}
else if (gOptions.advertisingMode == AdvertisingMode::kOperationalMultiAdmin)
{
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(
chip::Dnssd::OperationalAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetPeerId(PeerId().SetCompressedFabricId(gOptions.fabricId).SetNodeId(gOptions.nodeId)));
if (err == CHIP_NO_ERROR)
{
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(
chip::Dnssd::OperationalAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT + 1)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetPeerId(PeerId().SetCompressedFabricId(gOptions.fabricId + 1).SetNodeId(gOptions.nodeId + 1)));
}
if (err == CHIP_NO_ERROR)
{
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(
chip::Dnssd::OperationalAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT + 2)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetPeerId(PeerId().SetCompressedFabricId(gOptions.fabricId + 2).SetNodeId(gOptions.nodeId + 2)));
}
}
else if (gOptions.advertisingMode == AdvertisingMode::kCommissioner)
{
printf("Advertise Commissioner\n");
err = chip::Dnssd::ServiceAdvertiser::Instance().Advertise(
chip::Dnssd::CommissionAdvertisingParameters()
.EnableIpV4(gOptions.enableIpV4)
.SetPort(CHIP_PORT)
.SetMac(chip::ByteSpan(gOptions.mac, 6))
.SetVendorId(gOptions.vendorId)
.SetProductId(gOptions.productId)
.SetDeviceType(gOptions.deviceType)
.SetDeviceName(gOptions.deviceName)
.SetCommissionAdvertiseMode(chip::Dnssd::CommssionAdvertiseMode::kCommissioner));
}
else
{
fprintf(stderr, "FAILED to determine advertising type.\n");
return 1;
}
if (err != CHIP_NO_ERROR)
{
fprintf(stderr, "FAILED to setup advertisement parameters err=%s\n", chip::ErrorStr(err));
return 1;
}
signal(SIGTERM, StopSignalHandler);
signal(SIGINT, StopSignalHandler);
DeviceLayer::PlatformMgr().RunEventLoop();
tracing_setup.StopTracing();
Dnssd::Resolver::Instance().Shutdown();
DeviceLayer::PlatformMgr().Shutdown();
printf("Done...\n");
return 0;
}