0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 20:13:05 +02:00
openvpn3/openvpn/tun/tunio.hpp
Samuli Seppänen 04b2a3c9b7 Switch from AGPLv3 to GPLv3
Signed-off-by: Samuli Seppänen <samuli@openvpn.net>
2017-03-16 14:43:55 +02:00

277 lines
6.8 KiB
C++

// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2016 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Low level tun device I/O class for all platforms (Unix and Windows)
#ifndef OPENVPN_TUN_TUNIO_H
#define OPENVPN_TUN_TUNIO_H
#include <asio.hpp>
#include <openvpn/common/size.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/ip/ip.hpp>
#include <openvpn/common/socktypes.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/tun/tunlog.hpp>
namespace openvpn {
template <typename ReadHandler, typename PacketFrom, typename STREAM>
class TunIO : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TunIO> Ptr;
TunIO(ReadHandler read_handler_arg,
const Frame::Ptr& frame_arg,
const SessionStats::Ptr& stats_arg)
: stream(nullptr),
retain_stream(false),
tun_prefix(false),
halt(false),
read_handler(read_handler_arg),
frame(frame_arg),
frame_context((*frame_arg)[Frame::READ_TUN]),
stats(stats_arg)
{
}
virtual ~TunIO()
{
//OPENVPN_LOG("**** TUNIO destruct");
stop();
delete stream;
}
bool write(Buffer& buf)
{
if (!halt)
{
try {
// handle tun packet prefix, if enabled
if (tun_prefix)
{
if (buf.offset() >= 4 && buf.size() >= 1)
{
switch (IPHeader::version(buf[0]))
{
case 4:
prepend_pf_inet(buf, PF_INET);
break;
case 6:
prepend_pf_inet(buf, PF_INET6);
break;
default:
OPENVPN_LOG_TUN_ERROR("TUN write error: cannot identify IP version for prefix");
tun_error(Error::TUN_FRAMING_ERROR, nullptr);
return false;
}
}
else
{
OPENVPN_LOG_TUN_ERROR("TUN write error: cannot write prefix");
tun_error(Error::TUN_FRAMING_ERROR, nullptr);
return false;
}
}
// write data to tun device
const size_t wrote = stream->write_some(buf.const_buffers_1());
if (stats)
{
stats->inc_stat(SessionStats::TUN_BYTES_OUT, wrote);
stats->inc_stat(SessionStats::TUN_PACKETS_OUT, 1);
}
if (wrote == buf.size())
return true;
else
{
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
tun_error(Error::TUN_WRITE_ERROR, nullptr);
return false;
}
}
catch (asio::system_error& e)
{
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
tun_error(Error::TUN_WRITE_ERROR, &e.code());
return false;
}
}
else
return false;
}
template <class BUFSEQ>
bool write_seq(const BUFSEQ& bs)
{
if (!halt)
{
try {
// write data to tun device
const size_t wrote = stream->write_some(bs);
if (stats)
{
stats->inc_stat(SessionStats::TUN_BYTES_OUT, wrote);
stats->inc_stat(SessionStats::TUN_PACKETS_OUT, 1);
}
if (wrote == bs.size())
return true;
else
{
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
tun_error(Error::TUN_WRITE_ERROR, nullptr);
return false;
}
}
catch (asio::system_error& e)
{
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
tun_error(Error::TUN_WRITE_ERROR, &e.code());
return false;
}
}
else
return false;
}
void start(const int n_parallel)
{
if (!halt)
{
for (int i = 0; i < n_parallel; i++)
queue_read(nullptr);
}
}
// must be called by derived class destructor
void stop()
{
if (!halt)
{
halt = true;
if (stream)
{
stream->cancel();
if (!retain_stream)
{
//OPENVPN_LOG("**** TUNIO close");
stream->close();
}
else
stream->release();
}
}
}
std::string name() const
{
return name_;
}
private:
void prepend_pf_inet(Buffer& buf, const std::uint32_t value)
{
const std::uint32_t net_value = htonl(value);
buf.prepend((unsigned char *)&net_value, sizeof(net_value));
}
protected:
void queue_read(PacketFrom *tunfrom)
{
OPENVPN_LOG_TUN_VERBOSE("TunIO::queue_read");
if (!tunfrom)
tunfrom = new PacketFrom();
frame_context.prepare(tunfrom->buf);
// queue read on tun device
stream->async_read_some(frame_context.mutable_buffers_1(tunfrom->buf),
[self=Ptr(this), tunfrom](const asio::error_code& error, const size_t bytes_recvd)
{
self->handle_read(tunfrom, error, bytes_recvd);
});
}
void handle_read(PacketFrom *tunfrom, const asio::error_code& error, const size_t bytes_recvd)
{
OPENVPN_LOG_TUN_VERBOSE("TunIO::handle_read: " << error.message());
typename PacketFrom::SPtr pfp(tunfrom);
if (!halt)
{
if (!error)
{
pfp->buf.set_size(bytes_recvd);
if (stats)
{
stats->inc_stat(SessionStats::TUN_BYTES_IN, bytes_recvd);
stats->inc_stat(SessionStats::TUN_PACKETS_IN, 1);
}
if (!tun_prefix)
{
read_handler->tun_read_handler(pfp);
}
else if (pfp->buf.size() >= 4)
{
// handle tun packet prefix, if enabled
pfp->buf.advance(4);
read_handler->tun_read_handler(pfp);
}
else
{
OPENVPN_LOG_TUN_ERROR("TUN Read Error: cannot read prefix");
tun_error(Error::TUN_READ_ERROR, nullptr);
}
}
else
{
OPENVPN_LOG_TUN_ERROR("TUN Read Error: " << error.message());
tun_error(Error::TUN_READ_ERROR, &error);
}
if (!halt)
queue_read(pfp.release()); // reuse buffer if still available
}
}
void tun_error(const Error::Type errtype, const asio::error_code* error)
{
if (stats)
stats->error(errtype);
read_handler->tun_error_handler(errtype, error);
}
// should be set by derived class constructor
std::string name_;
STREAM *stream;
bool retain_stream; // don't close tun stream
bool tun_prefix;
bool halt;
ReadHandler read_handler;
const Frame::Ptr frame;
const Frame::Context& frame_context;
SessionStats::Ptr stats;
};
}
#endif