| /* |
| * |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * This file contains header for the Weave Tunnel Control Protocol, its state management |
| * and protocol operation functions. |
| * |
| * |
| */ |
| |
| #ifndef __STDC_FORMAT_MACROS |
| #define __STDC_FORMAT_MACROS |
| #endif // __STDC_FORMAT_MACROS |
| |
| #include "WeaveTunnelControl.h" |
| #include "WeaveTunnelAgent.h" |
| #include "WeaveTunnelConnectionMgr.h" |
| #include <InetLayer/InetInterface.h> |
| #include <SystemLayer/SystemTimer.h> |
| #include <Weave/Support/CodeUtils.h> |
| #include <Weave/Profiles/ProfileCommon.h> |
| #include <Weave/Core/WeaveServerBase.h> |
| #include <Weave/Core/WeaveTLV.h> |
| |
| #if WEAVE_CONFIG_ENABLE_TUNNELING |
| |
| using namespace nl::Inet; |
| using namespace nl::Weave::Profiles::WeaveTunnel; |
| using namespace nl::Weave::Profiles::StatusReporting; |
| using namespace nl::Weave::Encoding; |
| using namespace nl::Weave::TLV; |
| |
| WeaveTunnelControl::WeaveTunnelControl(void) |
| { |
| mTunnelAgent = NULL; |
| #if WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mShortcutTunExchangeCtxt = NULL; |
| #endif // WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mServiceExchangeCtxt = NULL; |
| mCtrlResponseTimeout = 0; |
| OnTunStatusRcvd = NULL; |
| } |
| |
| /** |
| * Initialize WeaveTunnelControl to set relevant members like the Weave Tunnel Agent and callbacks. |
| * |
| * @param[in] tunAgent A pointer to the WeaveTunnelAgent object. |
| * |
| * @param[in] statusRcvd A pointer to a callback for the StatusRcvd handler. |
| * |
| * @return WEAVE_NO_ERROR. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::Init(WeaveTunnelAgent *tunAgent, |
| TunnelStatusRcvdFunct statusRcvd) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| mTunnelAgent = tunAgent; |
| #if WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mShortcutTunExchangeCtxt = NULL; |
| mShortcutTunnelAdvInterval = WEAVE_CONFIG_TUNNELING_SHORTCUT_TUNNEL_ADV_INTERVAL_SECS; |
| memset(ShortcutTunnelPeerCache, 0, sizeof(ShortcutTunnelPeerCache)); |
| #endif // WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mServiceExchangeCtxt = NULL; |
| mCtrlResponseTimeout = WEAVE_CONFIG_TUNNELING_CTRL_RESPONSE_TIMEOUT_SECS; |
| OnTunStatusRcvd = statusRcvd; |
| |
| // Check if the WeaveTunnelAgent object is valid. |
| |
| VerifyOrExit(mTunnelAgent != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| err = mTunnelAgent->mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, |
| kMsgType_TunnelReconnect, |
| HandleTunnelReconnect, this); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * Close WeaveTunnelControl by closing any outstanding exchange contexts and resetting members. |
| * |
| * @return WEAVE_NO_ERROR. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::Close(void) |
| { |
| // Close all the exchanegContexts |
| |
| #if WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| if (mShortcutTunExchangeCtxt != NULL) |
| { |
| mShortcutTunExchangeCtxt->Close(); |
| } |
| #endif // WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| |
| if (mServiceExchangeCtxt != NULL) |
| { |
| mServiceExchangeCtxt->Close(); |
| } |
| |
| // Unregister the Tunnel reconnect handler |
| |
| mTunnelAgent->mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, |
| kMsgType_TunnelReconnect); |
| |
| Free(); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Send a Tunnel Open control message to the peer node with a set of tunnel routes. |
| * |
| * @param[in] conMgr A pointer to the WeaveTunnelConnectionMgr object. |
| * |
| * @param[in] tunRoutes List of prefix routes to add to route table. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendTunnelOpen(WeaveTunnelConnectionMgr *conMgr, |
| WeaveTunnelRoute *tunRoutes) |
| { |
| return SendTunnelMessage(kMsgType_TunnelOpenV2, conMgr, |
| mTunnelAgent->mExchangeMgr->FabricState->FabricId, |
| tunRoutes, HandleTunnelOpenResponse); |
| } |
| |
| /** |
| * Send a Tunnel Close control message to the peer node. |
| * |
| * @param[in] conMgr A pointer to the WeaveTunnelConnectionMgr object. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendTunnelClose (WeaveTunnelConnectionMgr *conMgr) |
| { |
| return SendTunnelMessage(kMsgType_TunnelClose, conMgr, |
| mTunnelAgent->mExchangeMgr->FabricState->FabricId, |
| NULL, HandleTunnelCloseResponse); |
| } |
| |
| /** |
| * Send a Tunnel Route Update control message to the peer node with a set of tunnel routes. |
| * |
| * @param[in] conMgr A pointer to the WeaveTunnelConnectionMgr object. |
| * |
| * @param[in] tunRoutes List of prefix routes to add to route table. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendTunnelRouteUpdate(WeaveTunnelConnectionMgr *conMgr, |
| WeaveTunnelRoute *tunRoutes) |
| { |
| return SendTunnelMessage(kMsgType_TunnelRouteUpdate, conMgr, |
| mTunnelAgent->mExchangeMgr->FabricState->FabricId, |
| tunRoutes, HandleTunnelRouteUpdateResponse); |
| } |
| |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| /** |
| * Send a Tunnel Liveness control message to the peer node. |
| * |
| * @param[in] conMgr A pointer to the WeaveTunnelConnectionMgr object. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendTunnelLiveness(WeaveTunnelConnectionMgr *conMgr) |
| { |
| return SendTunnelMessage(kMsgType_TunnelLiveness, conMgr, |
| 0, |
| NULL, HandleTunnelLivenessResponse); |
| } |
| #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| |
| #if WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| /** |
| * Fetch the interface for sending the broadcast advertisements |
| * |
| * @param[out] sendIntfId The send interface id for broadcasting advertisements. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::GetSendInterfaceIdForBroadcast (InterfaceId &sendIntfId) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| uint64_t globalId = 0; |
| IPAddress sendIntfAddr; |
| globalId = WeaveFabricIdToIPv6GlobalId(mTunnelAgent->mExchangeMgr->FabricState->FabricId); |
| if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_BorderGateway) |
| { |
| sendIntfAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_PrimaryWiFi, |
| WeaveNodeIdToIPv6InterfaceId(mTunnelAgent->mExchangeMgr->FabricState->LocalNodeId)); |
| } |
| else if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_MobileDevice) |
| { |
| sendIntfAddr = IPAddress::MakeULA(globalId, kWeaveSubnetId_MobileDevice, |
| WeaveNodeIdToIPv6InterfaceId(mTunnelAgent->mExchangeMgr->FabricState->LocalNodeId)); |
| } |
| res = mTunnelAgent->mInet->GetInterfaceFromAddr(sendIntfAddr, sendIntfId); |
| |
| return res; |
| } |
| |
| /* Send the Shortcut Tunnel Advertise message of specified type */ |
| WEAVE_ERROR WeaveTunnelControl::SendShortcutTunnelAdvertiseMessage (TunnelCtrlMsgType shortcutTunAdvMsgType, |
| InterfaceId sendIntfId, |
| uint64_t localAddrIdentifier) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer* msgBuf = NULL; |
| ExchangeContext* exchangeCtx = NULL; |
| uint8_t* p = NULL; |
| |
| // allocate buffer and send tunnel message |
| msgBuf = PacketBuffer::New(); |
| |
| VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| VerifyOrExit(mTunnelAgent->mExchangeMgr != NULL, |
| err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| exchangeCtx = mTunnelAgent->mExchangeMgr->NewContext(nl::Weave::kAnyNodeId, this); |
| |
| VerifyOrExit(exchangeCtx != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| mShortcutTunExchangeCtxt = exchangeCtx; |
| |
| //Encode the Fabric Id for Border Gateway and the Node Id for Mobile Client. |
| p = msgBuf->Start(); |
| LittleEndian::Write64(p, localAddrIdentifier); |
| msgBuf->SetDataLength(8); |
| mShortcutTunExchangeCtxt->PeerIntf = sendIntfId; |
| err = mShortcutTunExchangeCtxt->SendMessage(kWeaveProfile_Tunneling, shortcutTunAdvMsgType, msgBuf, |
| nl::Weave::ExchangeContext::kSendFlag_MulticastFromLinkLocal); |
| msgBuf = NULL; |
| SuccessOrExit(err); |
| |
| exit: |
| if (NULL != msgBuf) |
| { |
| PacketBuffer::Free(msgBuf); |
| } |
| |
| if (mShortcutTunExchangeCtxt != NULL) |
| { |
| mShortcutTunExchangeCtxt->Close(); |
| mShortcutTunExchangeCtxt = NULL; |
| } |
| |
| return err; |
| } |
| |
| /** |
| * Send a border router advertise message advertising its fabric Id. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendBorderRouterAdvertise (void) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| InterfaceId sendIntfId = INET_NULL_INTERFACEID; |
| |
| // Get the sendIntfId for the broadcast |
| |
| res = GetSendInterfaceIdForBroadcast(sendIntfId); |
| |
| if (res == WEAVE_NO_ERROR) |
| { |
| res = SendShortcutTunnelAdvertiseMessage(kMsgType_TunnelRouterAdvertise, sendIntfId, |
| mTunnelAgent->mExchangeMgr->FabricState->FabricId); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Send a mobile client advertise message advertising its Node Id. |
| * |
| * @return WEAVE_ERROR WEAVE_NO_ERROR on success, else error. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendMobileClientAdvertise (void) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| InterfaceId sendIntfId = INET_NULL_INTERFACEID; |
| |
| //Get the sendIntfId for the broadcast |
| res = GetSendInterfaceIdForBroadcast(sendIntfId); |
| |
| if (res == WEAVE_NO_ERROR) |
| { |
| res = SendShortcutTunnelAdvertiseMessage(kMsgType_TunnelMobileClientAdvertise, sendIntfId, |
| mTunnelAgent->mExchangeMgr->FabricState->LocalNodeId); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Function registered with WeaveMessageLayer for listening to Shortcut |
| * tunnel advertisments and updating cache. |
| * |
| * @param[in] ec A pointer to the ExchangeContext object. |
| * |
| * @param[in] pktInfo A pointer to the IPPacketInfo object. |
| * |
| * @param[in] msgInfo A pointer to the WeaveMessageInfo object. |
| * |
| * @param[in] profileId The profileId for which this handler was called. |
| * |
| * @param[in] msgType The msgType within the above profile. |
| * |
| * @param[in] payload A pointer to the PacketBuffer object holding the |
| * advertisement message. |
| * |
| * @return void |
| */ |
| void WeaveTunnelControl::HandleShortcutTunnelAdvertiseMessage (ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| uint8_t *p = NULL; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| uint64_t peerId = 0; |
| // Verify that the message is a Tunnel Control Message for added safety. |
| if (profileId != kWeaveProfile_Tunneling) |
| { |
| ExitNow(); |
| } |
| |
| p = payload->Start(); |
| peerId = LittleEndian::Read64(p); |
| |
| //Go through the message type and update the local nextHop Table |
| switch (msgType) |
| { |
| case kMsgType_TunnelRouterAdvertise: |
| //TunnelRouter advertisements are meant for mobile client devices. |
| if (tunControl->mTunnelAgent->mRole != WeaveTunnelAgent::kRole_MobileDevice) |
| { |
| ExitNow(); |
| } |
| |
| tunControl->UpdateOrAddTunnelPeerEntry(peerId, pktInfo->SrcAddress, |
| msgInfo->SourceNodeId); |
| |
| break; |
| case kMsgType_TunnelMobileClientAdvertise: |
| //Mobile Client advertisements are meant for tunnel border gateways. |
| if (tunControl->mTunnelAgent->mRole != WeaveTunnelAgent::kRole_BorderGateway) |
| { |
| ExitNow(); |
| } |
| |
| tunControl->UpdateOrAddTunnelPeerEntry(peerId, pktInfo->SrcAddress, |
| msgInfo->SourceNodeId); |
| |
| break; |
| default: |
| break; |
| } |
| |
| exit: |
| // Free the payload buffer. |
| PacketBuffer::Free(payload); |
| |
| // Discard the exchange context. |
| ec->Close(); |
| ec = NULL; |
| |
| return; |
| } |
| |
| /** |
| * Verify if the peer is present in the shortcut tunnel cache for sending locally. |
| * |
| * @param[in] peerId The identifier for the peer for the tunnel shortcut. |
| * It is the FabricId if the peer is a border gateway and |
| * the nodeId if the peer is a mobile device. |
| * |
| * @return true if present and false otherwise. |
| */ |
| bool WeaveTunnelControl::IsPeerInShortcutTunnelCache(uint64_t peerId) |
| { |
| int nextHopTableIndex = -1; |
| bool retVal = false; |
| nextHopTableIndex = FindTunnelPeerEntry(peerId); |
| |
| if (nextHopTableIndex >= 0) |
| { |
| retVal = true; |
| } |
| |
| return retVal; |
| } |
| |
| /** |
| * Enable shortcut tunneling by sending advertisments from either the Border |
| * gateway or Mobile client and also listening to advertisements from shortcut |
| * tunnel counterparts. |
| * |
| * @return void |
| */ |
| void WeaveTunnelControl::EnableShortcutTunneling (void) |
| { |
| RegisterShortcutTunnelAdvHandlers(); |
| |
| if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_BorderGateway) |
| { |
| StartShortcutTunnelAdvertisementsFromBorderRouter(); |
| } |
| else if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_MobileDevice) |
| { |
| StartShortcutTunnelAdvertisementsFromMobileClient(); |
| } |
| |
| StartNextHopTableMonitor(); |
| } |
| |
| /** |
| * Disable shortcut tunneling of sending advertisments from either the Border gateway or Mobile client and |
| * also listening to advertisements from shortcut tunnel counterparts. |
| * |
| * @return void |
| */ |
| void WeaveTunnelControl::DisableShortcutTunneling (void) |
| { |
| UnregisterShortcutTunnelAdvHandlers(); |
| |
| if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_BorderGateway) |
| { |
| StopShortcutTunnelAdvertisementsFromBorderRouter(); |
| } |
| else if (mTunnelAgent->mRole == WeaveTunnelAgent::kRole_MobileDevice) |
| { |
| StopShortcutTunnelAdvertisementsFromMobileClient(); |
| } |
| |
| StopNextHopTableMonitor(); |
| } |
| |
| /** |
| * Send message over the tunnel shortcut. |
| */ |
| WEAVE_ERROR WeaveTunnelControl::SendMessageOverTunnelShortcut(uint64_t peerId, |
| WeaveMessageInfo *msgInfo, |
| PacketBuffer *msg) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| int nextHopTableIndex = -1; |
| |
| nextHopTableIndex = FindTunnelPeerEntry(peerId); |
| |
| VerifyOrExit(nextHopTableIndex >= 0, err = WEAVE_ERROR_TUNNEL_PEER_ENTRY_NOT_FOUND); |
| |
| // For shortcut tunneling explicitly set the destination node id from neighbor cache |
| |
| msgInfo->DestNodeId = ShortcutTunnelPeerCache[nextHopTableIndex].peerNodeId; |
| |
| err = mTunnelAgent->mExchangeMgr->MessageLayer->SendUDPTunneledMessage( |
| ShortcutTunnelPeerCache[nextHopTableIndex].peerAddr, |
| msgInfo, msg); |
| msg = NULL; |
| |
| exit: |
| if (msg != NULL) |
| { |
| PacketBuffer::Free(msg); |
| #if WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS |
| // Update tunnel statistics |
| mTunnelAgent->mWeaveTunnelStats.mDroppedMessagesCount++; |
| #endif // WEAVE_CONFIG_TUNNEL_ENABLE_STATISTICS |
| } |
| |
| return err; |
| } |
| |
| /* Timer expiry function for sending periodic border router advertisements for shortcut tunneling */ |
| void WeaveTunnelControl::BorderRouterAdvTimeout (InetLayer *inetLayer, void *appState, INET_ERROR err) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(appState); |
| |
| // Send the Border Router Advertise message. |
| |
| tunControl->SendBorderRouterAdvertise(); |
| |
| // Restart the timer. |
| |
| res = tunControl->mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(tunControl->mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| BorderRouterAdvTimeout, tunControl); |
| VerifyOrDieWithMsg(res == WEAVE_NO_ERROR, WeaveTunnel, "Cannot start BorderRouterAdvTimeout\n"); |
| } |
| |
| /* Timer expiry function for sending periodic mobile client advertisements for shortcut tunneling */ |
| void WeaveTunnelControl::MobileClientAdvTimeout (InetLayer *inetLayer, void *appState, INET_ERROR err) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(appState); |
| |
| // Send the Mobile Client Advertise message. |
| |
| tunControl->SendMobileClientAdvertise(); |
| |
| // Restart the timer. |
| |
| res = tunControl->mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(tunControl->mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| MobileClientAdvTimeout, tunControl); |
| VerifyOrDieWithMsg(res == WEAVE_NO_ERROR, WeaveTunnel, "Cannot start MobileClientAdvTimeout\n"); |
| } |
| |
| /* Timer expiry functions to mark and purge stale entries from previous advertisements */ |
| void WeaveTunnelControl::PurgeStaleNextHopEntries (InetLayer *inetLayer, void *appState, INET_ERROR err) |
| { |
| WEAVE_ERROR res = WEAVE_NO_ERROR; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(appState); |
| |
| // Go through the shortcut tunnel nexthop table and purge stale entries. |
| |
| for (int i = 0; i < WEAVE_CONFIG_TUNNELING_MAX_NUM_SHORTCUT_TUNNEL_PEERS; i++) |
| { |
| if (tunControl->ShortcutTunnelPeerCache[i].peerIdentifier) |
| { |
| if (tunControl->ShortcutTunnelPeerCache[i].staleFlag) |
| { |
| // Remove stale entry |
| |
| tunControl->FreeNextHopEntry(i); |
| } |
| else |
| { |
| // Mark as stale |
| |
| tunControl->ShortcutTunnelPeerCache[i].staleFlag = true; |
| } |
| } |
| } |
| |
| // Restart the timer. |
| |
| res = tunControl->mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(tunControl->mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| PurgeStaleNextHopEntries, tunControl); |
| VerifyOrDieWithMsg(res == WEAVE_NO_ERROR, WeaveTunnel, "Cannot start PurgeStaleNextHopEntries\n"); |
| } |
| |
| /* Start the nexthop cache monitor */ |
| void WeaveTunnelControl::StartNextHopTableMonitor (void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| PurgeStaleNextHopEntries, this); |
| } |
| |
| /* Stop the nexthop cache monitor */ |
| void WeaveTunnelControl::StopNextHopTableMonitor (void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->CancelTimer(PurgeStaleNextHopEntries, this); |
| } |
| |
| /* Register the handlers for receving the shortcut tunnel advertisements for refreshing the nexthop cache */ |
| void WeaveTunnelControl::RegisterShortcutTunnelAdvHandlers (void) |
| { |
| mTunnelAgent->mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, kMsgType_TunnelRouterAdvertise, |
| HandleShortcutTunnelAdvertiseMessage, this); |
| |
| mTunnelAgent->mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, kMsgType_TunnelMobileClientAdvertise, |
| HandleShortcutTunnelAdvertiseMessage, this); |
| } |
| |
| /* Unregister the handlers for receving the shortcut tunnel advertisements for refreshing the nexthop cache */ |
| void WeaveTunnelControl::UnregisterShortcutTunnelAdvHandlers (void) |
| { |
| mTunnelAgent->mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, kMsgType_TunnelRouterAdvertise); |
| |
| mTunnelAgent->mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Tunneling, kMsgType_TunnelMobileClientAdvertise); |
| } |
| |
| /* Start sending the periodic border router advertisement messages */ |
| void WeaveTunnelControl::StartShortcutTunnelAdvertisementsFromBorderRouter(void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| BorderRouterAdvTimeout, this); |
| } |
| |
| /* Stop sending the periodic border router advertisement messages */ |
| void WeaveTunnelControl::StopShortcutTunnelAdvertisementsFromBorderRouter(void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->CancelTimer(BorderRouterAdvTimeout, this); |
| } |
| |
| /* Start sending the periodic mobile client advertisement messages */ |
| void WeaveTunnelControl::StartShortcutTunnelAdvertisementsFromMobileClient(void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->StartTimer(mShortcutTunnelAdvInterval * nl::Weave::System::kTimerFactor_milli_per_unit, |
| MobileClientAdvTimeout, this); |
| } |
| |
| /* Stop sending the periodic mobile client advertisement messages */ |
| void WeaveTunnelControl::StopShortcutTunnelAdvertisementsFromMobileClient(void) |
| { |
| mTunnelAgent->mExchangeMgr->MessageLayer->Inet->CancelTimer(MobileClientAdvTimeout, this); |
| } |
| |
| /* Lookup NextHop table entry */ |
| int WeaveTunnelControl::FindTunnelPeerEntry (uint64_t peerId) |
| { |
| int index = -1; |
| for (int i = 0; i < WEAVE_CONFIG_TUNNELING_MAX_NUM_SHORTCUT_TUNNEL_PEERS; i++) |
| { |
| if (peerId == ShortcutTunnelPeerCache[i].peerIdentifier) |
| { |
| index = i; |
| break; |
| } |
| } |
| |
| return index; |
| } |
| |
| /* Create a new route entry */ |
| int WeaveTunnelControl::NewNextHopEntry (void) |
| { |
| int retIndex = -1; |
| |
| for (int i = 0; i < WEAVE_CONFIG_TUNNELING_MAX_NUM_SHORTCUT_TUNNEL_PEERS; i++) |
| { |
| if (!ShortcutTunnelPeerCache[i].peerIdentifier) |
| { |
| retIndex = i; |
| break; |
| } |
| } |
| |
| return retIndex; |
| } |
| |
| /* Free a nexthop entry at a particular index */ |
| WEAVE_ERROR WeaveTunnelControl::FreeNextHopEntry (int index) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| if ((index < 0) || (index >= WEAVE_CONFIG_TUNNELING_MAX_NUM_SHORTCUT_TUNNEL_PEERS)) |
| { |
| ExitNow(); |
| } |
| |
| memset(&ShortcutTunnelPeerCache[index], 0, sizeof(ShortcutTunnelPeerEntry)); |
| |
| exit: |
| |
| return err; |
| } |
| |
| /* Lookup and update a nexthop entry or add a new entry */ |
| WEAVE_ERROR WeaveTunnelControl::UpdateOrAddTunnelPeerEntry (uint64_t peerId, IPAddress peerAddress, uint64_t peerNodeId) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| int8_t index = -1; |
| // Check and update the ShortcutTunnelPeerCache |
| |
| index = FindTunnelPeerEntry(peerId); |
| if (index < 0) |
| { |
| // Not found; Create a new entry |
| |
| index = NewNextHopEntry(); |
| if (index < 0) |
| { |
| ExitNow(err = WEAVE_ERROR_TUNNEL_NEXTHOP_TABLE_FULL); |
| } |
| } |
| |
| // Update the fields in the cache. |
| |
| ShortcutTunnelPeerCache[index].peerIdentifier = peerId; |
| ShortcutTunnelPeerCache[index].peerAddr = peerAddress; |
| ShortcutTunnelPeerCache[index].peerNodeId = peerNodeId; |
| ShortcutTunnelPeerCache[index].staleFlag = false; |
| |
| exit: |
| |
| return err; |
| } |
| #endif // WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| |
| /* Reset the members */ |
| void WeaveTunnelControl::Free (void) |
| { |
| mTunnelAgent = NULL; |
| #if WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mShortcutTunExchangeCtxt = NULL; |
| #endif // WEAVE_CONFIG_TUNNEL_SHORTCUT_SUPPORTED |
| mServiceExchangeCtxt = NULL; |
| OnTunStatusRcvd = NULL; |
| } |
| |
| /* Create an Exchange Context for exchanging Weave Control messages */ |
| WEAVE_ERROR WeaveTunnelControl::CreateContext (WeaveConnection *aConnection, |
| ExchangeContext::MessageReceiveFunct onMsgRcvd) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| ExchangeContext *exchangeCtx = NULL; |
| |
| VerifyOrExit(mTunnelAgent->mExchangeMgr != NULL, |
| err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| exchangeCtx = mTunnelAgent->mExchangeMgr->NewContext(aConnection, this); |
| |
| VerifyOrExit(exchangeCtx != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| // Assign the appropriate message receipt handler to the callback. |
| |
| exchangeCtx->OnMessageReceived = onMsgRcvd; |
| |
| exchangeCtx->ResponseTimeout = mCtrlResponseTimeout * nl::Weave::System::kTimerFactor_milli_per_unit; |
| exchangeCtx->OnResponseTimeout = TunCtrlRespExpiryHandler; |
| mServiceExchangeCtxt = exchangeCtx; |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveTunnelControl::VerifyAndParseStatusResponse(uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload, |
| StatusReport & outReport, |
| bool & outIsRoutingRestricted) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // Verify that message is a StatusReport |
| |
| VerifyOrExit(profileId == kWeaveProfile_Common, err = WEAVE_ERROR_INVALID_PROFILE_ID); |
| |
| VerifyOrExit(msgType == Common::kMsgType_StatusReport, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE); |
| |
| // Parse the StatusReport. |
| |
| err = StatusReport::parse(payload, outReport); |
| SuccessOrExit(err); |
| |
| VerifyOrExit(outReport.mProfileId == kWeaveProfile_Common && outReport.mStatusCode == Common::kStatus_Success, |
| err = WEAVE_ERROR_STATUS_REPORT_RECEIVED); |
| |
| // Check if there is TLV data in the StatusReport. |
| |
| if (outReport.mAdditionalInfo.theLength > 0) |
| { |
| err = ParseTunnelTLVData(outReport, outIsRoutingRestricted); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR WeaveTunnelControl::ParseTunnelTLVData(StatusReport & report, bool & outIsRoutingRestricted) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| TLVReader tunReader; |
| nl::Weave::TLV::TLVType OuterContainerType; |
| const uint8_t *tlvData = NULL; |
| uint16_t tlvDataLen; |
| uint64_t tag = 0; |
| |
| tlvData = report.mAdditionalInfo.theData; |
| tlvDataLen = report.mAdditionalInfo.theLength; |
| |
| // Verify that TLV data supplied by the Service is encapsulated in an anonymous |
| // TLV structure. All anonymous TLV structures begin with an anonymous structure control |
| // byte (0x15) and end with an end-of-container control byte (0x18). |
| |
| VerifyOrExit(tlvDataLen > 2, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(tlvData[0] == kTLVElementType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(tlvData[tlvDataLen - 1] == kTLVElementType_EndOfContainer, |
| err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| tunReader.Init(tlvData, tlvDataLen); |
| |
| err = tunReader.Next(); |
| SuccessOrExit(err); |
| |
| err = tunReader.EnterContainer(OuterContainerType); |
| SuccessOrExit(err); |
| |
| err = tunReader.Next(); |
| SuccessOrExit(err); |
| |
| tag = tunReader.GetTag(); |
| VerifyOrExit(nl::Weave::TLV::IsProfileTag(tag), |
| err = WEAVE_ERROR_INVALID_TLV_TAG); |
| |
| VerifyOrExit(nl::Weave::TLV::ProfileIdFromTag(tag) == kWeaveProfile_Tunneling, |
| err = WEAVE_ERROR_INVALID_TLV_TAG); |
| |
| VerifyOrExit(nl::Weave::TLV::TagNumFromTag(tag) == kTag_TunnelRoutingRestricted, |
| err = WEAVE_ERROR_INVALID_TLV_TAG); |
| |
| err = tunReader.Get(outIsRoutingRestricted); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| /* Handle received control messages */ |
| void WeaveTunnelControl::HandleTunnelOpenResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| StatusReport report; |
| bool isRoutingRestricted = false; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| WeaveTunnelConnectionMgr *connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| err = VerifyAndParseStatusResponse(profileId, msgType, payload, report, isRoutingRestricted); |
| |
| // Free the payload buffer and close the ExchangeContext early to free up resources. |
| |
| tunControl->FreeBufferAndCloseExchange(payload, tunControl->mServiceExchangeCtxt); |
| |
| SuccessOrExit(err); |
| |
| // Received a Tunnel Open Ack; Set the connection state |
| |
| connMgr->mConnectionState = WeaveTunnelConnectionMgr::kState_TunnelOpen; |
| |
| // Reset the failed connection attempts after a successful TunnelOpen. |
| |
| connMgr->mTunFailedConnAttemptsInRow = 0; |
| |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| // Tunnel is up. Start the Tunnel Liveness timer |
| |
| connMgr->StartLivenessTimer(); |
| #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| |
| // Call the TunnelOpen post processing function. |
| |
| tunControl->mTunnelAgent->WeaveTunnelConnectionUp(msgInfo, connMgr, isRoutingRestricted); |
| |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(WeaveTunnel, "HandleTunnelOpenResponse FAILED with error: %ld\n", (long)err); |
| |
| tunControl->TunnelCloseAndReportErrorStatus(connMgr, err, report); |
| } |
| |
| return; |
| } |
| |
| void WeaveTunnelControl::HandleTunnelCloseResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| StatusReport report; |
| bool isRoutingRestricted = false; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| WeaveTunnelConnectionMgr *connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| err = VerifyAndParseStatusResponse(profileId, msgType, payload, report, isRoutingRestricted); |
| |
| // Free the payload buffer and close the ExchangeContext early to free up resources. |
| |
| tunControl->FreeBufferAndCloseExchange(payload, tunControl->mServiceExchangeCtxt); |
| |
| SuccessOrExit(err); |
| |
| // Received successful response to a Tunnel control message |
| |
| |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| // Tunnel is closed. Stop the Tunnel Liveness timer |
| |
| connMgr->StopLivenessTimer(); |
| #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| |
| // Close the connection; Do not restart the connection as we had proactively issued a |
| // Tunnel Close to the peer. The application would need to start the tunnel explicitly. |
| |
| connMgr->StopServiceTunnelConn(WEAVE_NO_ERROR); |
| |
| // Received a Tunnel Close Ack: Call the TunnelClose post processing function. |
| |
| tunControl->mTunnelAgent->WeaveTunnelConnectionDown(connMgr, WEAVE_NO_ERROR); |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(WeaveTunnel, "HandleTunnelCloseResponse FAILED with error: %ld\n", (long)err); |
| |
| tunControl->TunnelCloseAndReportErrorStatus(connMgr, err, report); |
| } |
| |
| return; |
| } |
| |
| void WeaveTunnelControl::HandleTunnelRouteUpdateResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| StatusReport report; |
| bool isRoutingRestricted = false; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| WeaveTunnelConnectionMgr *connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| err = VerifyAndParseStatusResponse(profileId, msgType, payload, report, isRoutingRestricted); |
| |
| // Free the payload buffer and close the ExchangeContext early to free up resources. |
| |
| tunControl->FreeBufferAndCloseExchange(payload, tunControl->mServiceExchangeCtxt); |
| |
| SuccessOrExit(err); |
| |
| // Received successful response to a Tunnel control message |
| |
| // The connection state is kState_TunnelOpen |
| |
| connMgr->mConnectionState = WeaveTunnelConnectionMgr::kState_TunnelOpen; |
| |
| if (isRoutingRestricted) |
| { |
| // Although tunnel is restricted, it is still open but can only be |
| // usable by the border gateway for itself to access a limited set of |
| // Service endpoints. The device is put in this mode, typically, when |
| // it is unpaired from the account. |
| connMgr->mTunAgent->RemovePlatformTunnelRoute(); |
| |
| WeaveLogDetail(WeaveTunnel, "Tunnel in restricted mode; Not operating as a Border Router\n"); |
| } |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(WeaveTunnel, "HandleTunnelRouteUpdateResponse FAILED with error: %ld\n", (long)err); |
| |
| tunControl->TunnelCloseAndReportErrorStatus(connMgr, err, report); |
| } |
| |
| return; |
| } |
| |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| void WeaveTunnelControl::HandleTunnelLivenessResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| StatusReport report; |
| bool isRoutingRestricted = false; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| WeaveTunnelConnectionMgr *connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| err = VerifyAndParseStatusResponse(profileId, msgType, payload, report, isRoutingRestricted); |
| |
| // Free the payload buffer and close the ExchangeContext early to free up resources. |
| |
| tunControl->FreeBufferAndCloseExchange(payload, tunControl->mServiceExchangeCtxt); |
| |
| SuccessOrExit(err); |
| |
| // Received successful response to a Tunnel control message |
| |
| // Tunnel is alive. Schedule the next Tunnel Liveness timer. |
| |
| connMgr->StartLivenessTimer(); |
| |
| // Notify the application of the successful Liveness probe response. |
| |
| connMgr->mTunAgent->NotifyTunnelLiveness(connMgr->mTunType, WEAVE_NO_ERROR); |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| WeaveLogError(WeaveTunnel, "HandleTunnelLivenessResponse FAILED with error: %ld\n", (long)err); |
| |
| tunControl->TunnelCloseAndReportErrorStatus(connMgr, err, report); |
| } |
| |
| return; |
| } |
| #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| |
| /** |
| * Handler to reconnect with the peer node. |
| * |
| */ |
| void WeaveTunnelControl::HandleTunnelReconnect(ExchangeContext *ec, const IPPacketInfo *pktInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| WeaveTunnelConnectionMgr *connMgr = NULL; |
| WeaveTunnelControl *tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| uint16_t hostPort = 0; |
| char hostName[255]; // Per spec, max DNS name length is 253. |
| uint8_t hostLen = sizeof(hostName); |
| uint16_t payloadLen = 0; |
| |
| VerifyOrExit(ec->Con != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| payloadLen = payload->DataLength(); |
| |
| // Set the connection state |
| |
| connMgr->mConnectionState = WeaveTunnelConnectionMgr::kState_ReconnectRecvd; |
| |
| // Send a status report |
| |
| err = SendStatusReport(ec, kWeaveProfile_Common, Common::kStatus_Success); |
| SuccessOrExit(err); |
| |
| // Set the hostname to NULL string |
| |
| hostName[0] = '\0'; |
| |
| if (payloadLen) |
| { |
| // Fetch the hostname and port |
| |
| err = tunControl->DecodeTunnelReconnect(hostPort, hostName, hostLen, payload); |
| SuccessOrExit(err); |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY |
| if (tunControl->mTunnelAgent->mServiceMgr) |
| { |
| if (0 == payloadLen) |
| { |
| // No hostname specified. Clear cache and force trip to directory server |
| // during reconnect. |
| |
| tunControl->mTunnelAgent->mServiceMgr->clearCache(); |
| |
| } |
| else |
| { |
| // Received a new hostname and port. Add a new Service directory entry |
| |
| err = tunControl->mTunnelAgent->mServiceMgr->replaceOrAddCacheEntry(hostPort, hostName, hostLen, |
| kServiceEndpoint_WeaveTunneling); |
| SuccessOrExit(err); |
| } |
| } |
| else |
| #endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY |
| { |
| // Store the Service address for connecting directly if the hostname is |
| // a DNS resolved IP address in string format. |
| |
| if (IPAddress::FromString(hostName, tunControl->mTunnelAgent->mServiceAddress)) |
| { |
| // store the port also if the hostName is a resolved IP address string. |
| |
| tunControl->mTunnelAgent->mServicePort = hostPort; |
| } |
| } |
| |
| // Close the tunnel connection |
| |
| connMgr->StopServiceTunnelConn(WEAVE_NO_ERROR); |
| |
| // Reset the failed connection attempts counter as this is a fresh reconnect. |
| |
| connMgr->mTunFailedConnAttemptsInRow = 0; |
| |
| // Try reconnecting to the Service. |
| |
| connMgr->ScheduleConnect(CONNECT_NO_DELAY); |
| |
| tunControl->mTunnelAgent->WeaveTunnelServiceReconnectRequested(connMgr, hostName, hostPort); |
| exit: |
| |
| tunControl->FreeBufferAndCloseExchange(payload, ec); |
| |
| return; |
| } |
| |
| void WeaveTunnelControl::TunnelCloseAndReportErrorStatus(WeaveTunnelConnectionMgr *connMgr, |
| WEAVE_ERROR err, |
| StatusReport report) |
| { |
| |
| ReconnectParam reconnParam; |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| // Tunnel is being closed. Stop the Tunnel Liveness timer. |
| |
| connMgr->StopLivenessTimer(); |
| #endif // WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| |
| // Report the status |
| |
| if (OnTunStatusRcvd != NULL) |
| { |
| OnTunStatusRcvd(connMgr->mTunType, report); |
| } |
| |
| if (connMgr->mConnectionState == WeaveTunnelConnectionMgr::kState_TunnelClosing) |
| { |
| // Close the connection |
| |
| connMgr->StopServiceTunnelConn(err); |
| |
| // Callback to WeaveTunnelAgent to notify application if it has already closed the tunnel. |
| |
| mTunnelAgent->WeaveTunnelConnectionDown(connMgr, err); |
| } |
| else |
| { |
| // Close the connection and reconnect |
| |
| reconnParam.PopulateReconnectParam(err, |
| report.mProfileId, |
| report.mStatusCode); |
| |
| connMgr->StopAndReconnectTunnelConn(reconnParam); |
| } |
| |
| return; |
| } |
| |
| void WeaveTunnelControl::FreeBufferAndCloseExchange(PacketBuffer *buf, ExchangeContext *&ec) |
| { |
| // Free the payload buffer. |
| |
| if (buf) |
| { |
| PacketBuffer::Free(buf); |
| } |
| |
| // Discard the exchange context. |
| if (ec) |
| { |
| ec->Close(); |
| ec = NULL; |
| } |
| |
| return; |
| } |
| |
| /* Send a tunnel control status report message */ |
| WEAVE_ERROR WeaveTunnelControl::SendStatusReport(ExchangeContext *ec, uint32_t profileId, |
| uint16_t tunStatusCode) |
| { |
| WEAVE_ERROR err; |
| |
| err = nl::Weave::WeaveServerBase::SendStatusReport(ec, profileId, tunStatusCode, WEAVE_NO_ERROR, 0); |
| |
| return err; |
| } |
| |
| |
| /* Send the Tunnel Route Control message of specified type */ |
| WEAVE_ERROR WeaveTunnelControl::SendTunnelMessage(TunnelCtrlMsgType msgType, |
| WeaveTunnelConnectionMgr *conMgr, |
| uint64_t fabricId, |
| WeaveTunnelRoute *tunRoutes, |
| ExchangeContext::MessageReceiveFunct onMsgRcvd) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| PacketBuffer* msgBuf = NULL; |
| uint8_t *p = NULL; |
| |
| // allocate buffer and send tunnel message |
| |
| msgBuf = PacketBuffer::New(); |
| |
| VerifyOrExit(msgBuf != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| // Create an ExchangeContext |
| |
| err = CreateContext(conMgr->mServiceCon, onMsgRcvd); |
| SuccessOrExit(err); |
| |
| // A Tunnel Liveness is an empty tunnel control message. |
| |
| if (msgType != kMsgType_TunnelLiveness) |
| { |
| |
| if (msgType == kMsgType_TunnelOpenV2) |
| { |
| p = msgBuf->Start(); |
| |
| VerifyOrExit(msgBuf->AvailableDataLength() >= NL_TUNNEL_AGENT_ROLE_SIZE_IN_BYTES + |
| NL_TUNNEL_TYPE_SIZE_IN_BYTES + NL_TUNNEL_SRC_INTF_TYPE_SIZE_IN_BYTES + |
| NL_TUNNEL_LIVENESS_TYPE_SIZE_IN_BYTES + NL_TUNNEL_LIVENESS_MAX_TIMEOUT_SIZE_IN_BYTES, |
| err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| |
| // Encode the tunnel device role, tunnel type, and source interface type in the TunnelOpen message. |
| |
| Write8(p, conMgr->mTunAgent->mRole); |
| |
| Write8(p, conMgr->mTunType); |
| |
| Write8(p, conMgr->mSrcInterfaceType); |
| |
| #if WEAVE_CONFIG_TUNNEL_LIVENESS_SUPPORTED |
| Write8(p, kLiveness_TunnelControl); |
| |
| LittleEndian::Write16(p, conMgr->mTunnelLivenessInterval); |
| #elif WEAVE_CONFIG_TUNNEL_TCP_KEEPALIVE_SUPPORTED |
| Write8(p, kLiveness_TCPKeepAlive); |
| |
| LittleEndian::Write16(p, conMgr->mKeepAliveIntervalSecs * (conMgr->mMaxNumProbes + 1)); |
| #endif |
| |
| // Tunnel device role(1 byte) + Tunnel type(1 byte) + Tunnel source interface type(1 byte) + |
| // Tunnel liveness strategy(1 byte) + Tunnel Liveness Max timeout(2 bytes) |
| |
| msgBuf->SetDataLength(1 + 1 + 1 + 1 + 2); |
| } |
| |
| err = WeaveTunnelRoute::EncodeFabricTunnelRoutes(fabricId, |
| tunRoutes, msgBuf); |
| SuccessOrExit(err); |
| } |
| |
| err = mServiceExchangeCtxt->SendMessage(kWeaveProfile_Tunneling, msgType, msgBuf, |
| ExchangeContext::kSendFlag_ExpectResponse); |
| msgBuf = NULL; |
| SuccessOrExit(err); |
| |
| exit: |
| if (NULL != msgBuf) |
| { |
| PacketBuffer::Free(msgBuf); |
| } |
| |
| if (err != WEAVE_NO_ERROR && mServiceExchangeCtxt != NULL) |
| { |
| mServiceExchangeCtxt->Close(); |
| mServiceExchangeCtxt = NULL; |
| } |
| |
| return err; |
| |
| } |
| |
| /* Tunnel connection closing handler for tunnel control message error paths and |
| * response timeouts. |
| */ |
| void WeaveTunnelControl::TunCtrlRespExpiryHandler(ExchangeContext *ec) |
| { |
| WeaveTunnelConnectionMgr *connMgr = NULL; |
| WeaveTunnelControl *tunControl = NULL; |
| ReconnectParam reconnParam; |
| |
| if (ec) |
| { |
| tunControl = static_cast<WeaveTunnelControl *>(ec->AppState); |
| |
| if (ec->Con) |
| { |
| connMgr = static_cast<WeaveTunnelConnectionMgr *>(ec->Con->AppState); |
| |
| // Set the ExchangeContext for this connMgr to NULL; |
| |
| connMgr->mTunControl.mServiceExchangeCtxt = NULL; |
| |
| |
| if (connMgr->mConnectionState == WeaveTunnelConnectionMgr::kState_TunnelClosing) |
| { |
| // Stop the connection. |
| |
| connMgr->StopServiceTunnelConn(WEAVE_ERROR_TIMEOUT); |
| |
| // Callback to WeaveTunnelAgent to notify application if it has already closed the tunnel. |
| |
| tunControl->mTunnelAgent->WeaveTunnelConnectionDown(connMgr, WEAVE_ERROR_TIMEOUT); |
| } |
| else |
| { |
| // Stop the connection and attempt to reconnect. |
| |
| reconnParam.PopulateReconnectParam(WEAVE_ERROR_TIMEOUT); |
| |
| connMgr->StopAndReconnectTunnelConn(reconnParam); |
| } |
| } |
| |
| // Close the ExchangeContext |
| |
| ec->Close(); |
| ec = NULL; |
| } |
| |
| } |
| |
| /** |
| * Decode the Tunnel Reconnect message from the Service and update the |
| * Service Directory with the new Tunnel Endpoint hostname and port. |
| * |
| * @note |
| * hostNameLen is passed in with the size of the buffer hostName and |
| * is set to the length of the hostName(decoded from the PacketBuffer) |
| * by this function as output. |
| * |
| */ |
| WEAVE_ERROR WeaveTunnelControl::DecodeTunnelReconnect(uint16_t &hostPort, |
| char *hostName, |
| uint8_t &hostNameLen, |
| PacketBuffer *msg) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| uint16_t msgLen = msg->DataLength(); |
| uint8_t *p = NULL; |
| |
| p = msg->Start(); |
| |
| // Verify that we can read the port, which is 2 bytes. |
| |
| VerifyOrExit(msgLen > 2, err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH); |
| |
| hostPort = LittleEndian::Read16(p); |
| |
| // Verify that the buffer has enough space for the hostname |
| |
| VerifyOrExit(hostNameLen > msgLen - 2, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| // Assign the actual length of the hostname. |
| |
| hostNameLen = msgLen - 2; |
| |
| // Read the hostname string into the buffer provided |
| |
| memcpy(hostName, p, hostNameLen); |
| hostName[hostNameLen] = '\0'; |
| |
| exit: |
| return err; |
| } |
| #endif // WEAVE_CONFIG_ENABLE_TUNNELING |