blob: f6af2be4d69315660de7cf599a8a02413e49fcd4 [file] [log] [blame]
/*
*
* 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 implements the WeaveConnectionTunnel class to manage
* Weave communication tunneled between a pair of WeaveConnection
* objects that are coupled together.
*
*/
#include <Weave/Core/WeaveCore.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/CodeUtils.h>
namespace nl {
namespace Weave {
void WeaveConnectionTunnel::Init(WeaveMessageLayer *messageLayer)
{
// Die if tunnel already initialized.
VerifyOrDie(mMessageLayer == NULL);
mMessageLayer = messageLayer;
}
WEAVE_ERROR WeaveConnectionTunnel::MakeTunnelConnected(TCPEndPoint *endPointOne, TCPEndPoint *endPointTwo)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(mMessageLayer != NULL && endPointOne != NULL && endPointTwo != NULL && endPointOne != endPointTwo,
err = WEAVE_ERROR_INCORRECT_STATE);
mEPOne = endPointOne;
mEPTwo = endPointTwo;
mEPOne->AppState = this;
mEPTwo->AppState = this;
// Hang callbacks on TCPEndPoints to set up data and connection closure forwarding between both ends of the tunnel.
mEPOne->OnDataReceived = HandleTunnelDataReceived;
mEPOne->OnConnectionClosed = HandleTunnelConnectionClosed;
mEPOne->OnPeerClose = HandleReceiveShutdown;
mEPTwo->OnDataReceived = HandleTunnelDataReceived;
mEPTwo->OnConnectionClosed = HandleTunnelConnectionClosed;
mEPTwo->OnPeerClose = HandleReceiveShutdown;
exit:
return err;
}
void WeaveConnectionTunnel::CloseEndPoint(TCPEndPoint **endPoint)
{
// Close and free specified TCPEndPoint.
if (*endPoint != NULL)
{
if ((*endPoint)->Close() != WEAVE_NO_ERROR)
{
(*endPoint)->Abort();
}
(*endPoint)->Free();
*endPoint = NULL;
}
}
/**
* Shutdown the WeaveConnectionTunnel by closing the component endpoints which, in turn,
* close the corresponding TCP connections. This function terminates the tunnel and any
* further use of a WeaveConnectionTunnel needs to be initiated by a call to
* WeaveMessageLayer::NewConnectionTunnel();
*
*/
void WeaveConnectionTunnel::Shutdown()
{
WeaveLogProgress(ExchangeManager, "Shutting down tunnel %04X with EP (%04X, %04X)", LogId(),
mEPOne->LogId(), mEPTwo->LogId());
// Die if tunnel uninitialized.
VerifyOrDie(mMessageLayer != NULL);
CloseEndPoint(&mEPOne);
CloseEndPoint(&mEPTwo);
if (OnShutdown != NULL)
{
OnShutdown(this);
OnShutdown = NULL;
}
mMessageLayer = NULL;
}
static void PrintTunnelInfo(const WeaveConnectionTunnel &tun, const TCPEndPoint &fromEndPoint, const TCPEndPoint &toEndPoint, const PacketBuffer &data)
{
IPAddress addr;
uint16_t port;
WEAVE_ERROR err;
char fromIpAddrStr[INET6_ADDRSTRLEN], toIpAddrStr[INET6_ADDRSTRLEN];
err = fromEndPoint.GetPeerInfo(&addr, &port);
SuccessOrExit(err);
addr.ToString(fromIpAddrStr, sizeof(fromIpAddrStr));
err = toEndPoint.GetPeerInfo(&addr, &port);
SuccessOrExit(err);
addr.ToString(toIpAddrStr, sizeof(toIpAddrStr));
WeaveLogDetail(ExchangeManager, "Forwarding %u bytes on tunnel %04X from %s -> %s\n", data.DataLength(), tun.LogId(), fromIpAddrStr, toIpAddrStr);
exit:
return;
}
void WeaveConnectionTunnel::HandleTunnelDataReceived(TCPEndPoint *fromEndPoint, PacketBuffer *data)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveConnectionTunnel *tun = (WeaveConnectionTunnel *)fromEndPoint->AppState;
TCPEndPoint *toEndPoint;
if (tun == NULL || (fromEndPoint != tun->mEPOne && fromEndPoint != tun->mEPTwo))
{
err = WEAVE_ERROR_INCORRECT_STATE;
goto exit;
}
toEndPoint = fromEndPoint == tun->mEPOne ? tun->mEPTwo : tun->mEPOne;
PrintTunnelInfo(*tun, *fromEndPoint, *toEndPoint, *data);
// Misnomer - AckReceive doesn't explicitly ack anything, it just enlarges our TCP receive window.
err = fromEndPoint->AckReceive(data->DataLength());
SuccessOrExit(err);
// Forward received data to other end of the tunnel.
err = toEndPoint->Send(data, true);
data = NULL;
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(ExchangeManager, "Err forwarding data on tunnel %04X, err = %d", (tun ? tun->LogId() : 0), err);
if (data != NULL)
PacketBuffer::Free(data);
}
}
void WeaveConnectionTunnel::HandleTunnelConnectionClosed(TCPEndPoint *endPoint, INET_ERROR err)
{
WeaveConnectionTunnel *tun = (WeaveConnectionTunnel *)endPoint->AppState;
if (tun == NULL || (endPoint != tun->mEPOne && endPoint != tun->mEPTwo))
{
WeaveLogDetail(ExchangeManager, "Got tunnel endpoint closed with bad state");
return;
}
// Close both ends of the tunnel and free this tunnel.
tun->Shutdown();
}
void WeaveConnectionTunnel::HandleReceiveShutdown(TCPEndPoint *endPoint)
{
WeaveConnectionTunnel *tun = (WeaveConnectionTunnel *)endPoint->AppState;
if (tun == NULL)
{
WeaveLogDetail(ExchangeManager, "Null AppState in HandleReceiveShutdown");
return;
}
WeaveLogProgress(ExchangeManager, "Forwarding half-closure on tunnel %04X from EP %04X", tun->LogId(),
endPoint->LogId());
// Reflect half-closure on other end of tunnel.
if (endPoint == tun->mEPOne)
{
tun->mEPTwo->Shutdown();
}
else if (endPoint == tun->mEPTwo)
{
tun->mEPOne->Shutdown();
}
else
{
WeaveLogDetail(ExchangeManager, "Got half-close on tunnel %04X for unknown endpoint %04X", tun->LogId(),
endPoint->LogId());
return;
}
}
} // namespace nl
} // namespace Weave