void WebSocketTransport::doSend(Transport::Packet&& packet) { NFD_LOG_FACE_TRACE(__func__); websocketpp::lib::error_code error; m_server.send(m_handle, packet.packet.wire(), packet.packet.size(), websocketpp::frame::opcode::binary, error); if (error) return processErrorCode(error); NFD_LOG_FACE_TRACE("Successfully sent: " << packet.packet.size() << " bytes"); }
void DatagramTransport<T, U>::receiveDatagram(const uint8_t* buffer, size_t nBytesReceived, const boost::system::error_code& error) { if (error) return processErrorCode(error); NFD_LOG_FACE_TRACE("Received: " << nBytesReceived << " bytes from " << m_sender); bool isOk = false; Block element; std::tie(isOk, element) = Block::fromBuffer(buffer, nBytesReceived); if (!isOk) { NFD_LOG_FACE_WARN("Failed to parse incoming packet from " << m_sender); // This packet won't extend the face lifetime return; } if (element.size() != nBytesReceived) { NFD_LOG_FACE_WARN("Received datagram size and decoded element size don't match"); // This packet won't extend the face lifetime return; } m_hasRecentlyReceived = true; Transport::Packet tp(std::move(element)); tp.remoteEndpoint = makeEndpointId(m_sender); this->receive(std::move(tp)); }
void TcpTransport::reconnect() { NFD_LOG_FACE_TRACE(__func__); if (getState() == TransportState::CLOSING || getState() == TransportState::FAILED || getState() == TransportState::CLOSED) { // transport is shutting down, don't attempt to reconnect return; } BOOST_ASSERT(getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT); BOOST_ASSERT(getState() == TransportState::DOWN); // recreate the socket m_socket = protocol::socket(m_socket.get_io_service()); this->resetReceiveBuffer(); this->resetSendQueue(); m_reconnectEvent = scheduler::schedule(m_nextReconnectWait, [this] { handleReconnectTimeout(); }); m_socket.async_connect(m_remoteEndpoint, [this] (const boost::system::error_code& error) { handleReconnect(error); }); }
void WebSocketTransport::handlePong() { NFD_LOG_FACE_TRACE(__func__); ++this->nInPongs; }
void GenericLinkService::doReceivePacket(const Block& packet, const EndpointId& endpoint) { try { lp::Packet pkt(packet); if (m_options.reliabilityOptions.isEnabled) { m_reliability.processIncomingPacket(pkt); } if (!pkt.has<lp::FragmentField>()) { NFD_LOG_FACE_TRACE("received IDLE packet: DROP"); return; } if ((pkt.has<lp::FragIndexField>() || pkt.has<lp::FragCountField>()) && !m_options.allowReassembly) { NFD_LOG_FACE_WARN("received fragment, but reassembly disabled: DROP"); return; } bool isReassembled = false; Block netPkt; lp::Packet firstPkt; std::tie(isReassembled, netPkt, firstPkt) = m_reassembler.receiveFragment(endpoint, pkt); if (isReassembled) { this->decodeNetPacket(netPkt, firstPkt, endpoint); } } catch (const tlv::Error& e) { ++this->nInLpInvalid; NFD_LOG_FACE_WARN("packet parse error (" << e.what() << "): DROP"); } }
void DatagramTransport<T, U>::handleSend(const boost::system::error_code& error, size_t nBytesSent) { if (error) return processErrorCode(error); NFD_LOG_FACE_TRACE("Successfully sent: " << nBytesSent << " bytes"); }
void DatagramTransport<T, U>::doSend(Transport::Packet&& packet) { NFD_LOG_FACE_TRACE(__func__); m_socket.async_send(boost::asio::buffer(packet.packet), // packet.packet is copied into the lambda to retain the underlying Buffer [this, p = packet.packet] (auto&&... args) { this->handleSend(std::forward<decltype(args)>(args)...); }); }
void GenericLinkService::checkCongestionLevel(lp::Packet& pkt) { ssize_t sendQueueLength = getTransport()->getSendQueueLength(); // This operation requires that the transport supports retrieving current send queue length if (sendQueueLength < 0) { return; } // To avoid overflowing the queue, set the congestion threshold to at least half of the send // queue capacity. size_t congestionThreshold = m_options.defaultCongestionThreshold; if (getTransport()->getSendQueueCapacity() >= 0) { congestionThreshold = std::min(congestionThreshold, static_cast<size_t>(getTransport()->getSendQueueCapacity()) / DEFAULT_CONGESTION_THRESHOLD_DIVISOR); } if (sendQueueLength > 0) { NFD_LOG_FACE_TRACE("txqlen=" << sendQueueLength << " threshold=" << congestionThreshold << " capacity=" << getTransport()->getSendQueueCapacity()); } if (static_cast<size_t>(sendQueueLength) > congestionThreshold) { // Send queue is congested const auto now = time::steady_clock::now(); if (now >= m_nextMarkTime || now >= m_lastMarkTime + m_options.baseCongestionMarkingInterval) { // Mark at most one initial packet per baseCongestionMarkingInterval if (m_nMarkedSinceInMarkingState == 0) { m_nextMarkTime = now; } // Time to mark packet pkt.set<lp::CongestionMarkField>(1); ++nCongestionMarked; NFD_LOG_FACE_DEBUG("LpPacket was marked as congested"); ++m_nMarkedSinceInMarkingState; // Decrease the marking interval by the inverse of the square root of the number of packets // marked in this incident of congestion m_nextMarkTime += time::nanoseconds(static_cast<time::nanoseconds::rep>( m_options.baseCongestionMarkingInterval.count() / std::sqrt(m_nMarkedSinceInMarkingState))); m_lastMarkTime = now; } } else if (m_nextMarkTime != time::steady_clock::TimePoint::max()) { // Congestion incident has ended, so reset NFD_LOG_FACE_DEBUG("Send queue length dropped below congestion threshold"); m_nextMarkTime = time::steady_clock::TimePoint::max(); m_nMarkedSinceInMarkingState = 0; } }
void WebSocketTransport::doClose() { NFD_LOG_FACE_TRACE(__func__); m_pingEventId.cancel(); // use the non-throwing variant and ignore errors, if any websocketpp::lib::error_code error; m_server.close(m_handle, websocketpp::close::status::normal, "closed by NFD", error); this->setState(TransportState::CLOSED); }
void WebSocketTransport::sendPing() { NFD_LOG_FACE_TRACE(__func__); ++this->nOutPings; websocketpp::lib::error_code error; m_server.ping(m_handle, "NFD-WebSocket", error); if (error) return processErrorCode(error); this->schedulePing(); }
void WebSocketTransport::receiveMessage(const std::string& msg) { NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes"); bool isOk = false; Block element; std::tie(isOk, element) = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()), msg.size()); if (!isOk) { NFD_LOG_FACE_WARN("Failed to parse message payload"); return; } this->receive(Transport::Packet(std::move(element))); }
bool Transport::canChangePersistencyTo(ndn::nfd::FacePersistency newPersistency) const { // not changing, or setting initial persistency in subclass constructor if (m_persistency == newPersistency || m_persistency == ndn::nfd::FACE_PERSISTENCY_NONE) { return true; } if (newPersistency == ndn::nfd::FACE_PERSISTENCY_NONE) { NFD_LOG_FACE_TRACE("cannot change persistency to NONE"); return false; } return this->canChangePersistencyToImpl(newPersistency); }
void TcpTransport::handleReconnect(const boost::system::error_code& error) { if (getState() == TransportState::CLOSING || getState() == TransportState::FAILED || getState() == TransportState::CLOSED || error == boost::asio::error::operation_aborted) { // transport is shutting down, abort the reconnection attempt and ignore any errors return; } if (error) { NFD_LOG_FACE_TRACE("Reconnection attempt failed: " << error.message()); return; } m_reconnectEvent.cancel(); m_nextReconnectWait = s_initialReconnectWait; this->setLocalUri(FaceUri(m_socket.local_endpoint())); NFD_LOG_FACE_TRACE("TCP connection reestablished"); this->setState(TransportState::UP); this->startReceive(); }
void WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error) { NFD_LOG_FACE_TRACE(__func__); if (getState() == TransportState::CLOSING || getState() == TransportState::FAILED || getState() == TransportState::CLOSED) // transport is shutting down, ignore any errors return; NFD_LOG_FACE_WARN("Send or ping operation failed: " << error.message()); this->setState(TransportState::FAILED); doClose(); }
void TcpTransport::handleError(const boost::system::error_code& error) { if (this->getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) { NFD_LOG_FACE_TRACE("TCP socket error: " << error.message()); this->setState(TransportState::DOWN); // cancel all outstanding operations boost::system::error_code error; m_socket.cancel(error); // do this asynchronously because there could be some callbacks still pending getGlobalIoService().post([this] { reconnect(); }); } else { StreamTransport::handleError(error); } }
void DatagramTransport<T, U>::doClose() { NFD_LOG_FACE_TRACE(__func__); if (m_socket.is_open()) { // Cancel all outstanding operations and close the socket. // Use the non-throwing variants and ignore errors, if any. boost::system::error_code error; m_socket.cancel(error); m_socket.close(error); } // Ensure that the Transport stays alive at least until // all pending handlers are dispatched getGlobalIoService().post([this] { this->setState(TransportState::CLOSED); }); }
void Transport::send(const Block& packet, const EndpointId& endpoint) { BOOST_ASSERT(packet.isValid()); BOOST_ASSERT(this->getMtu() == MTU_UNLIMITED || packet.size() <= static_cast<size_t>(this->getMtu())); TransportState state = this->getState(); if (state != TransportState::UP && state != TransportState::DOWN) { NFD_LOG_FACE_TRACE("send ignored in " << state << " state"); return; } if (state == TransportState::UP) { ++this->nOutPackets; this->nOutBytes += packet.size(); } this->doSend(packet, endpoint); }
void DatagramTransport<T, U>::processErrorCode(const boost::system::error_code& error) { NFD_LOG_FACE_TRACE(__func__); if (getState() == TransportState::CLOSING || getState() == TransportState::FAILED || getState() == TransportState::CLOSED || error == boost::asio::error::operation_aborted) { // transport is shutting down, ignore any errors return; } if (getPersistency() == ndn::nfd::FACE_PERSISTENCY_PERMANENT) { NFD_LOG_FACE_DEBUG("Permanent face ignores error: " << error.message()); return; } NFD_LOG_FACE_ERROR("Send or receive operation failed: " << error.message()); this->setState(TransportState::FAILED); doClose(); }
std::tuple<bool, Block, lp::Packet> LpReassembler::receiveFragment(Transport::EndpointId remoteEndpoint, const lp::Packet& packet) { BOOST_ASSERT(packet.has<lp::FragmentField>()); static auto FALSE_RETURN = std::make_tuple(false, Block(), lp::Packet()); // read and check FragIndex and FragCount uint64_t fragIndex = 0; uint64_t fragCount = 1; if (packet.has<lp::FragIndexField>()) { fragIndex = packet.get<lp::FragIndexField>(); } if (packet.has<lp::FragCountField>()) { fragCount = packet.get<lp::FragCountField>(); } if (fragIndex >= fragCount) { NFD_LOG_FACE_WARN("reassembly error, FragIndex>=FragCount: DROP"); return FALSE_RETURN; } if (fragCount > m_options.nMaxFragments) { NFD_LOG_FACE_WARN("reassembly error, FragCount over limit: DROP"); return FALSE_RETURN; } // check for fast path if (fragIndex == 0 && fragCount == 1) { ndn::Buffer::const_iterator fragBegin, fragEnd; std::tie(fragBegin, fragEnd) = packet.get<lp::FragmentField>(); Block netPkt(&*fragBegin, std::distance(fragBegin, fragEnd)); return std::make_tuple(true, netPkt, packet); } // check Sequence and compute message identifier if (!packet.has<lp::SequenceField>()) { NFD_LOG_FACE_WARN("reassembly error, Sequence missing: DROP"); return FALSE_RETURN; } lp::Sequence messageIdentifier = packet.get<lp::SequenceField>() - fragIndex; Key key = std::make_tuple(remoteEndpoint, messageIdentifier); // add to PartialPacket PartialPacket& pp = m_partialPackets[key]; if (pp.fragCount == 0) { // new PartialPacket pp.fragCount = fragCount; pp.nReceivedFragments = 0; pp.fragments.resize(fragCount); } else { if (fragCount != pp.fragCount) { NFD_LOG_FACE_WARN("reassembly error, FragCount changed: DROP"); return FALSE_RETURN; } } if (pp.fragments[fragIndex].has<lp::SequenceField>()) { NFD_LOG_FACE_TRACE("fragment already received: DROP"); return FALSE_RETURN; } pp.fragments[fragIndex] = packet; ++pp.nReceivedFragments; // check complete condition if (pp.nReceivedFragments == pp.fragCount) { Block reassembled = doReassembly(key); lp::Packet firstFrag(std::move(pp.fragments[0])); m_partialPackets.erase(key); return std::make_tuple(true, reassembled, firstFrag); } // set drop timer pp.dropTimer = scheduler::schedule(m_options.reassemblyTimeout, [=] { timeoutPartialPacket(key); }); return FALSE_RETURN; }