|  | /* | 
|  | * | 
|  | *    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; | 
|  | } |