/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** * Copyright (C) 2014 Named Data Networking Project * See COPYING for copyright and distribution information. */ #include "face-manager.hpp" #include "core/face-uri.hpp" #include "core/network-interface.hpp" #include "fw/face-table.hpp" #include "face/tcp-factory.hpp" #include "face/udp-factory.hpp" #ifdef HAVE_UNIX_SOCKETS #include "face/unix-stream-factory.hpp" #endif // HAVE_UNIX_SOCKETS #ifdef HAVE_PCAP #include "face/ethernet-factory.hpp" #endif // HAVE_PCAP namespace nfd { NFD_LOG_INIT("FaceManager"); const Name FaceManager::COMMAND_PREFIX("/localhost/nfd/faces"); const size_t FaceManager::COMMAND_UNSIGNED_NCOMPS = FaceManager::COMMAND_PREFIX.size() + 1 + // verb 1; // verb options const size_t FaceManager::COMMAND_SIGNED_NCOMPS = FaceManager::COMMAND_UNSIGNED_NCOMPS + 4; // (timestamp, nonce, signed info tlv, signature tlv) const FaceManager::SignedVerbAndProcessor FaceManager::SIGNED_COMMAND_VERBS[] = { SignedVerbAndProcessor( Name::Component("create"), &FaceManager::createFace ), SignedVerbAndProcessor( Name::Component("destroy"), &FaceManager::destroyFace ), }; const FaceManager::UnsignedVerbAndProcessor FaceManager::UNSIGNED_COMMAND_VERBS[] = { UnsignedVerbAndProcessor( Name::Component("list"), &FaceManager::listFaces ), }; const Name FaceManager::LIST_COMMAND_PREFIX("/localhost/nfd/faces/list"); const size_t FaceManager::LIST_COMMAND_NCOMPS = LIST_COMMAND_PREFIX.size(); FaceManager::FaceManager(FaceTable& faceTable, shared_ptr face) : ManagerBase(face, FACE_MANAGER_PRIVILEGE) , m_faceTable(faceTable) , m_statusPublisher(m_faceTable, m_face, LIST_COMMAND_PREFIX) , m_signedVerbDispatch(SIGNED_COMMAND_VERBS, SIGNED_COMMAND_VERBS + (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor))) , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS, UNSIGNED_COMMAND_VERBS + (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor))) { face->setInterestFilter("/localhost/nfd/faces", bind(&FaceManager::onFaceRequest, this, _2)); } FaceManager::~FaceManager() { } void FaceManager::setConfigFile(ConfigFile& configFile) { configFile.addSectionHandler("face_system", bind(&FaceManager::onConfig, this, _1, _2, _3)); } void FaceManager::onConfig(const ConfigSection& configSection, bool isDryRun, const std::string& filename) { bool hasSeenUnix = false; bool hasSeenTcp = false; bool hasSeenUdp = false; bool hasSeenEther = false; const std::list > nicList(listNetworkInterfaces()); for (ConfigSection::const_iterator item = configSection.begin(); item != configSection.end(); ++item) { if (item->first == "unix") { if (hasSeenUnix) throw Error("Duplicate \"unix\" section"); hasSeenUnix = true; processSectionUnix(item->second, isDryRun); } else if (item->first == "tcp") { if (hasSeenTcp) throw Error("Duplicate \"tcp\" section"); hasSeenTcp = true; processSectionTcp(item->second, isDryRun); } else if (item->first == "udp") { if (hasSeenUdp) throw Error("Duplicate \"udp\" section"); hasSeenUdp = true; processSectionUdp(item->second, isDryRun, nicList); } else if (item->first == "ether") { if (hasSeenEther) throw Error("Duplicate \"ether\" section"); hasSeenEther = true; processSectionEther(item->second, isDryRun, nicList); } else { throw Error("Unrecognized option \"" + item->first + "\""); } } } void FaceManager::processSectionUnix(const ConfigSection& configSection, bool isDryRun) { // ; the unix section contains settings of UNIX stream faces and channels // unix // { // listen yes ; set to 'no' to disable UNIX stream listener, default 'yes' // path /var/run/nfd.sock ; UNIX stream listener path // } #if defined(HAVE_UNIX_SOCKETS) bool needToListen = true; std::string path = "/var/run/nfd.sock"; for (ConfigSection::const_iterator i = configSection.begin(); i != configSection.end(); ++i) { if (i->first == "path") { path = i->second.get_value(); } else if (i->first == "listen") { needToListen = parseYesNo(i, i->first, "unix"); } else { throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"unix\" section"); } } if (!isDryRun) { shared_ptr factory = make_shared(); shared_ptr unixChannel = factory->createChannel(path); if (needToListen) { // Should acceptFailed callback be used somehow? unixChannel->listen(bind(&FaceTable::add, &m_faceTable, _1), UnixStreamChannel::ConnectFailedCallback()); } m_factories.insert(std::make_pair("unix", factory)); } #else throw ConfigFile::Error("NFD was compiled without UNIX sockets support, cannot process \"unix\" section"); #endif // HAVE_UNIX_SOCKETS } void FaceManager::processSectionTcp(const ConfigSection& configSection, bool isDryRun) { // ; the tcp section contains settings of TCP faces and channels // tcp // { // listen yes ; set to 'no' to disable TCP listener, default 'yes' // port 6363 ; TCP listener port number // } std::string port = "6363"; bool needToListen = true; for (ConfigSection::const_iterator i = configSection.begin(); i != configSection.end(); ++i) { if (i->first == "port") { port = i->second.get_value(); } else if (i->first == "listen") { needToListen = parseYesNo(i, i->first, "tcp"); } else { throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"tcp\" section"); } } if (!isDryRun) { shared_ptr factory = make_shared(boost::cref(port)); using namespace boost::asio::ip; shared_ptr ipv4Channel = factory->createChannel("0.0.0.0", port); shared_ptr ipv6Channel = factory->createChannel("::", port); if (needToListen) { // Should acceptFailed callback be used somehow? ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), TcpChannel::ConnectFailedCallback()); ipv6Channel->listen(bind(&FaceTable::add, &m_faceTable, _1), TcpChannel::ConnectFailedCallback()); } m_factories.insert(std::make_pair("tcp", factory)); m_factories.insert(std::make_pair("tcp4", factory)); m_factories.insert(std::make_pair("tcp6", factory)); } } void FaceManager::processSectionUdp(const ConfigSection& configSection, bool isDryRun, const std::list >& nicList) { // ; the udp section contains settings of UDP faces and channels // udp // { // port 6363 ; UDP unicast port number // idle_timeout 30 ; idle time (seconds) before closing a UDP unicast face // keep_alive_interval 25; interval (seconds) between keep-alive refreshes // ; NFD creates one UDP multicast face per NIC // mcast yes ; set to 'no' to disable UDP multicast, default 'yes' // mcast_port 56363 ; UDP multicast port number // mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only) // } std::string port = "6363"; size_t timeout = 30; size_t keepAliveInterval = 25; bool useMcast = true; std::string mcastGroup = "224.0.23.170"; std::string mcastPort = "56363"; for (ConfigSection::const_iterator i = configSection.begin(); i != configSection.end(); ++i) { if (i->first == "port") { port = i->second.get_value(); } else if (i->first == "idle_timeout") { try { timeout = i->second.get_value(); } catch (const std::exception& e) { throw ConfigFile::Error("Invalid value for option \"" + i->first + "\" in \"udp\" section"); } } else if (i->first == "keep_alive_interval") { try { keepAliveInterval = i->second.get_value(); } catch (const std::exception& e) { throw ConfigFile::Error("Invalid value for option \"" + i->first + "\" in \"udp\" section"); } } else if (i->first == "mcast") { useMcast = parseYesNo(i, i->first, "udp"); } else if (i->first == "mcast_port") { mcastPort = i->second.get_value(); } else if (i->first == "mcast_group") { using namespace boost::asio::ip; mcastGroup = i->second.get_value(); try { address mcastGroupTest = address::from_string(mcastGroup); if (!mcastGroupTest.is_v4()) { throw ConfigFile::Error("Invalid value for option \"" + i->first + "\" in \"udp\" section"); } } catch(const std::runtime_error& e) { throw ConfigFile::Error("Invalid value for option \"" + i->first + "\" in \"udp\" section"); } } else { throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"udp\" section"); } } /// \todo what is keep alive interval used for? if (!isDryRun) { shared_ptr factory = make_shared(boost::cref(port)); factory->createChannel("::", port, time::seconds(timeout)); m_factories.insert(std::make_pair("udp", factory)); m_factories.insert(std::make_pair("udp4", factory)); m_factories.insert(std::make_pair("udp6", factory)); if (useMcast) { bool useEndpoint = false; udp::Endpoint localEndpoint; try { localEndpoint.port(boost::lexical_cast(port)); useEndpoint = true; } catch (const boost::bad_lexical_cast& error) { NFD_LOG_DEBUG("Treating UDP port \"" << port << "\" as a service name"); } for (std::list >::const_iterator i = nicList.begin(); i != nicList.end(); ++i) { const shared_ptr& nic = *i; if (nic->isUp() && nic->isMulticastCapable() && !nic->ipv4Addresses.empty()) { shared_ptr newFace = factory->createMulticastFace(nic->ipv4Addresses[0].to_string(), mcastGroup, mcastPort); NFD_LOG_INFO("Created multicast Face ID " << newFace->getId()); if (useEndpoint) { for (std::vector::const_iterator j = nic->ipv4Addresses.begin(); j != nic->ipv4Addresses.end(); ++j) { localEndpoint.address(*j); factory->createChannel(localEndpoint, time::seconds(timeout)); } } else { for (std::vector::const_iterator j = nic->ipv4Addresses.begin(); j != nic->ipv4Addresses.end(); ++j) { factory->createChannel(j->to_string(), port, time::seconds(timeout)); } } } } } else { factory->createChannel("0.0.0.0", port, time::seconds(timeout)); } } } void FaceManager::processSectionEther(const ConfigSection& configSection, bool isDryRun, const std::list >& nicList) { // ; the ether section contains settings of Ethernet faces and channels // ether // { // ; NFD creates one Ethernet multicast face per NIC // mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes' // mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group // } #if defined(HAVE_PCAP) using ethernet::Address; bool useMcast = true; Address mcastGroup(ethernet::getDefaultMulticastAddress()); for (ConfigSection::const_iterator i = configSection.begin(); i != configSection.end(); ++i) { if (i->first == "mcast") { useMcast = parseYesNo(i, i->first, "ether"); } else if (i->first == "mcast_group") { mcastGroup = Address::fromString(i->second.get_value()); if (mcastGroup.isNull()) { throw ConfigFile::Error("Invalid value for option \"" + i->first + "\" in \"ether\" section"); } } else { throw ConfigFile::Error("Unrecognized option \"" + i->first + "\" in \"ether\" section"); } } if (!isDryRun) { shared_ptr factory = make_shared(); m_factories.insert(std::make_pair("ether", factory)); if (useMcast) { for (std::list >::const_iterator i = nicList.begin(); i != nicList.end(); ++i) { const shared_ptr& nic = *i; if (nic->isUp() && nic->isMulticastCapable()) { try { shared_ptr newFace = factory->createMulticastFace(nic->name, mcastGroup); NFD_LOG_INFO("Created multicast Face ID " << newFace->getId()); } catch (const EthernetFactory::Error& factoryError) { NFD_LOG_ERROR(factoryError.what() << ", continuing"); } catch (const EthernetFace::Error& faceError) { NFD_LOG_ERROR(faceError.what() << ", continuing"); } } } } } #else throw ConfigFile::Error("NFD was compiled without libpcap, cannot process \"ether\" section"); #endif // HAVE_PCAP } void FaceManager::onFaceRequest(const Interest& request) { const Name& command = request.getName(); const size_t commandNComps = command.size(); const Name::Component& verb = command.get(COMMAND_PREFIX.size()); UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb); if (unsignedVerbProcessor != m_unsignedVerbDispatch.end()) { NFD_LOG_INFO("command result: processing verb: " << verb); (unsignedVerbProcessor->second)(this, boost::cref(request)); } else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps && commandNComps < COMMAND_SIGNED_NCOMPS) { NFD_LOG_INFO("command result: unsigned verb: " << command); sendResponse(command, 401, "Signature required"); } else if (commandNComps < COMMAND_SIGNED_NCOMPS || !COMMAND_PREFIX.isPrefixOf(command)) { NFD_LOG_INFO("command result: malformed"); sendResponse(command, 400, "Malformed command"); } else { validate(request, bind(&FaceManager::onValidatedFaceRequest, this, _1), bind(&ManagerBase::onCommandValidationFailed, this, _1, _2)); } } void FaceManager::onValidatedFaceRequest(const shared_ptr& request) { const Name& command = request->getName(); const Name::Component& verb = command.get(COMMAND_PREFIX.size()); SignedVerbDispatchTable::const_iterator signedVerbProcessor = m_signedVerbDispatch.find(verb); if (signedVerbProcessor != m_signedVerbDispatch.end()) { ndn::nfd::FaceManagementOptions options; if (!extractOptions(*request, options)) { sendResponse(command, 400, "Malformed command"); return; } NFD_LOG_INFO("command result: processing verb: " << verb); (signedVerbProcessor->second)(this, command, options); } else { NFD_LOG_INFO("command result: unsupported verb: " << verb); sendResponse(command, 501, "Unsupported command"); } } bool FaceManager::extractOptions(const Interest& request, ndn::nfd::FaceManagementOptions& extractedOptions) { const Name& command = request.getName(); const size_t optionCompIndex = COMMAND_PREFIX.size() + 1; try { Block rawOptions = request.getName()[optionCompIndex].blockFromValue(); extractedOptions.wireDecode(rawOptions); } catch (const ndn::Tlv::Error& e) { NFD_LOG_INFO("Bad command option parse: " << command); return false; } NFD_LOG_DEBUG("Options parsed OK"); return true; } void FaceManager::onCreated(const Name& requestName, ndn::nfd::FaceManagementOptions& options, const shared_ptr& newFace) { m_faceTable.add(newFace); options.setFaceId(newFace->getId()); NFD_LOG_INFO("Created Face ID " << newFace->getId()); ndn::nfd::ControlResponse response; setResponse(response, 200, "Success", options.wireEncode()); sendResponse(requestName, response); } void FaceManager::onConnectFailed(const Name& requestName, const std::string& reason) { NFD_LOG_INFO("Failed to create face: " << reason); sendResponse(requestName, 400, "Failed to create face"); } void FaceManager::createFace(const Name& requestName, ndn::nfd::FaceManagementOptions& options) { FaceUri uri; if (!uri.parse(options.getUri())) { sendResponse(requestName, 400, "Malformed command"); return; } FactoryMap::iterator factory = m_factories.find(uri.getScheme()); if (factory == m_factories.end()) { sendResponse(requestName, 501, "Unsupported protocol"); return; } factory->second->createFace(uri, bind(&FaceManager::onCreated, this, requestName, options, _1), bind(&FaceManager::onConnectFailed, this, requestName, _1)); } void FaceManager::destroyFace(const Name& requestName, ndn::nfd::FaceManagementOptions& options) { shared_ptr target = m_faceTable.get(options.getFaceId()); if (target) { // don't call m_faceTable.remove(target): it's called by target->close() via onFail target->close(); } sendResponse(requestName, 200, "Success"); } void FaceManager::listFaces(const Interest& request) { const Name& command = request.getName(); const size_t commandNComps = command.size(); if (commandNComps < LIST_COMMAND_NCOMPS || !LIST_COMMAND_PREFIX.isPrefixOf(command)) { NFD_LOG_INFO("command result: malformed"); sendResponse(command, 400, "Malformed command"); return; } m_statusPublisher.publish(); } } // namespace nfd