0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00

Completed client refactoring to make polymorphic abstraction

layer for transport and tun objects.
This commit is contained in:
James Yonan 2012-01-04 15:58:31 +00:00
parent d7039586dd
commit 648298d17a
11 changed files with 367 additions and 144 deletions

View File

@ -2,6 +2,7 @@
#define OPENVPN_COMMON_DISPATCH_H
#include <openvpn/common/types.hpp>
#include <openvpn/common/rc.hpp>
namespace openvpn {
// Dispatcher for asio async_read
@ -15,12 +16,12 @@ namespace openvpn {
void operator()(const boost::system::error_code& error, const size_t bytes_recvd)
{
(obj_->*handle_read_)(data_, error, bytes_recvd);
(obj_.get()->*handle_read_)(data_, error, bytes_recvd);
}
private:
Handler handle_read_;
C* obj_;
boost::intrusive_ptr<C> obj_;
Data data_;
};
@ -41,12 +42,12 @@ namespace openvpn {
void operator()(const boost::system::error_code& error)
{
(obj_->*handler_)(data_, error);
(obj_.get()->*handler_)(data_, error);
}
private:
Handler handler_;
C* obj_;
boost::intrusive_ptr<C> obj_;
Data data_;
};
@ -67,12 +68,12 @@ namespace openvpn {
void operator()(const boost::system::error_code& error)
{
(obj_->*handler_)(error);
(obj_.get()->*handler_)(error);
}
private:
Handler handler_;
C* obj_;
boost::intrusive_ptr<C> obj_;
};
template <typename C, typename Handler>
@ -92,14 +93,39 @@ namespace openvpn {
void operator()(const boost::system::error_code& error, EndpointIterator iter)
{
(obj_->*handler_)(error, iter);
(obj_.get()->*handler_)(error, iter);
}
private:
Handler handler_;
C* obj_;
boost::intrusive_ptr<C> obj_;
};
// Dispatcher for asio signal
template <typename C, typename Handler>
class AsioDispatchSignal
{
public:
AsioDispatchSignal(Handler handler, C* obj)
: handler_(handler), obj_(obj) {}
void operator()(const boost::system::error_code& error, int signal_number)
{
(obj_.get()->*handler_)(error, signal_number);
}
private:
Handler handler_;
boost::intrusive_ptr<C> obj_;
};
template <typename C, typename Handler>
AsioDispatchSignal<C, Handler> asio_dispatch_signal(Handler handler, C* obj)
{
return AsioDispatchSignal<C, Handler>(handler, obj);
}
// General purpose dispatcher with data
template <typename C, typename Handler, typename Data>

View File

@ -3,6 +3,7 @@
#include <queue>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
@ -10,7 +11,6 @@
#include <openvpn/common/thread.hpp>
#include <openvpn/log/log.hpp>
namespace openvpn {
class ASIOThreadPool

View File

@ -24,6 +24,7 @@ namespace openvpn {
// error stats
NETWORK_ERROR, // errors on network socket
RESOLVE_ERROR, // DNS resolution error
BAD_SRC_ADDR, // packet from unknown source address
TUN_ERROR, // errors on tun/tap interface
HMAC_ERROR, // HMAC verification failure
REPLAY_ERROR, // error from PacketIDReceive
@ -82,6 +83,7 @@ namespace openvpn {
"TUN_BYTES_OUT",
"NETWORK_ERROR",
"RESOLVE_ERROR",
"BAD_SRC_ADDR",
"TUN_ERROR",
"HMAC_ERROR",
"REPLAY_ERROR",

View File

@ -6,8 +6,8 @@
#include <boost/lexical_cast.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/common/process.hpp>
#include <openvpn/common/file.hpp>

View File

@ -1,10 +1,11 @@
#ifndef OPENVPN_TRANSPORT_TRANSBASE_H
#define OPENVPN_TRANSPORT_TRANSBASE_H
#ifndef OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H
#define OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H
#include <string>
#include <boost/asio.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
namespace openvpn {
@ -25,7 +26,7 @@ namespace openvpn {
{
virtual void transport_recv(BufferAllocated& buf) = 0;
virtual void transport_connected() {}
virtual void transport_error(const std::string) {}
virtual void transport_error(const std::exception&) {}
};
struct TransportClientFactory : public RC<thread_unsafe_refcount>
@ -38,4 +39,4 @@ namespace openvpn {
} // namespace openvpn
#endif // OPENVPN_TRANSPORT_TRANSBASE_H
#endif // OPENVPN_TRANSPORT_CLIENT_TRANSBASE_H

View File

@ -1,16 +1,18 @@
#ifndef OPENVPN_TRANSPORT_UDPCLI_H
#define OPENVPN_TRANSPORT_UDPCLI_H
#ifndef OPENVPN_TRANSPORT_CLIENT_UDPCLI_H
#define OPENVPN_TRANSPORT_CLIENT_UDPCLI_H
#include <sstream>
#include <boost/asio.hpp>
#include <openvpn/transport/udplink.hpp>
#include <openvpn/transport/transbase.hpp>
#include <openvpn/transport/client/transbase.hpp>
namespace openvpn {
namespace UDPTransport {
OPENVPN_EXCEPTION(udp_transport_resolve_error);
class ClientConfig : public TransportClientFactory
{
public:
@ -18,6 +20,7 @@ namespace openvpn {
std::string server_host;
std::string server_port;
bool server_addr_float;
int n_parallel;
Frame::Ptr frame;
ProtoStats::Ptr stats;
@ -30,7 +33,8 @@ namespace openvpn {
virtual TransportClient::Ptr new_client_obj(boost::asio::io_service& io_service,
TransportClientParent& parent);
private:
ClientConfig() {}
ClientConfig()
: server_addr_float(false), n_parallel(8) {}
};
class Client : public TransportClient
@ -88,7 +92,6 @@ namespace openvpn {
: io_service(io_service_arg),
config(config_arg),
parent(parent_arg),
impl(NULL),
resolver(io_service_arg),
halt(false)
{
@ -104,7 +107,10 @@ namespace openvpn {
void udp_read_handler(PacketFrom::SPtr& pfp) // called by LinkImpl
{
parent.transport_recv(pfp->buf);
if (config->server_addr_float || pfp->sender_endpoint == server_endpoint)
parent.transport_recv(pfp->buf);
else
config->stats->error(ProtoStats::BAD_SRC_ADDR);
}
void stop_()
@ -112,9 +118,9 @@ namespace openvpn {
if (impl)
{
impl->stop();
delete impl;
impl = NULL;
impl.reset();
}
resolver.cancel();
halt = true;
}
@ -128,13 +134,13 @@ namespace openvpn {
// get resolved endpoint
server_endpoint = *endpoint_iterator;
impl = new LinkImpl(io_service,
this,
server_endpoint,
REMOTE_CONNECT,
false,
config->frame,
config->stats);
impl.reset(new LinkImpl(io_service,
this,
server_endpoint,
REMOTE_CONNECT,
false,
config->frame,
config->stats));
impl->start(config->n_parallel);
parent.transport_connected();
}
@ -144,7 +150,8 @@ namespace openvpn {
os << "DNS resolve error on '" << config->server_host << "' for UDP session: " << error;
config->stats->error(ProtoStats::RESOLVE_ERROR);
stop();
parent.transport_error(os.str());
udp_transport_resolve_error err(os.str());
parent.transport_error(err);
}
}
}
@ -152,7 +159,7 @@ namespace openvpn {
boost::asio::io_service& io_service;
ClientConfig::Ptr config;
TransportClientParent& parent;
LinkImpl* impl;
LinkImpl::Ptr impl;
boost::asio::ip::udp::resolver resolver;
UDPTransport::Endpoint server_endpoint;
bool halt;

View File

@ -1,12 +1,12 @@
#ifndef OPENVPN_TRANSPORT_UDPLINK_H
#define OPENVPN_TRANSPORT_UDPLINK_H
#include <boost/noncopyable.hpp>
#include <boost/asio.hpp>
#include <openvpn/common/types.hpp>
#include <openvpn/common/scoped_ptr.hpp>
#include <openvpn/common/dispatch.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/log/log.hpp>
#include <openvpn/log/protostats.hpp>
@ -41,9 +41,11 @@ namespace openvpn {
};
template <typename ReadHandler>
class Link : boost::noncopyable
class Link : public RC<thread_unsafe_refcount>
{
public:
typedef boost::intrusive_ptr<Link> Ptr;
Link(boost::asio::io_service& io_service,
ReadHandler read_handler_arg,
const Endpoint& endpoint,
@ -73,39 +75,52 @@ namespace openvpn {
bool send(const Buffer& buf, Endpoint* endpoint)
{
try {
const size_t wrote = endpoint
? socket.send_to(buf.const_buffers_1(), *endpoint)
: socket.send(buf.const_buffers_1());
stats->inc_stat(ProtoStats::BYTES_OUT, wrote);
if (wrote == buf.size())
return true;
else
{
OPENVPN_LOG_UDPLINK_ERROR("UDP partial send error");
stats->error(ProtoStats::NETWORK_ERROR);
return false;
}
}
catch (boost::system::system_error& e)
if (!halt)
{
OPENVPN_LOG_UDPLINK_ERROR("UDP send error: " << e.what());
stats->error(ProtoStats::NETWORK_ERROR);
return false;
try {
const size_t wrote = endpoint
? socket.send_to(buf.const_buffers_1(), *endpoint)
: socket.send(buf.const_buffers_1());
stats->inc_stat(ProtoStats::BYTES_OUT, wrote);
if (wrote == buf.size())
return true;
else
{
OPENVPN_LOG_UDPLINK_ERROR("UDP partial send error");
stats->error(ProtoStats::NETWORK_ERROR);
return false;
}
}
catch (boost::system::system_error& e)
{
OPENVPN_LOG_UDPLINK_ERROR("UDP send error: " << e.what());
stats->error(ProtoStats::NETWORK_ERROR);
return false;
}
}
else
return false;
}
void start(const int n_parallel)
{
for (int i = 0; i < n_parallel; i++)
queue_read(NULL);
if (!halt)
{
for (int i = 0; i < n_parallel; i++)
queue_read(NULL);
}
}
void stop() {
halt = true;
socket.close();
if (!halt)
{
halt = true;
socket.close();
}
}
~Link() { stop(); }
private:
void queue_read(PacketFrom *udpfrom)
{

View File

@ -0,0 +1,41 @@
#ifndef OPENVPN_TUN_CLIENT_TUNBASE_H
#define OPENVPN_TUN_CLIENT_TUNBASE_H
#include <string>
#include <boost/asio.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/transport/client/transbase.hpp>
namespace openvpn {
struct TunClient : public RC<thread_unsafe_refcount>
{
typedef boost::intrusive_ptr<TunClient> Ptr;
virtual void client_start(const OptionList&, TransportClient&) = 0;
virtual void stop() = 0;
virtual bool tun_send(BufferAllocated& buf) = 0;
virtual std::string tun_name() const = 0;
};
struct TunClientParent
{
virtual void tun_recv(BufferAllocated& buf) = 0;
virtual void tun_connected() {}
virtual void tun_error(const std::exception&) {}
};
struct TunClientFactory : public RC<thread_unsafe_refcount>
{
typedef boost::intrusive_ptr<TunClientFactory> Ptr;
virtual TunClient::Ptr new_client_obj(boost::asio::io_service& io_service,
TunClientParent& parent) = 0;
};
} // namespace openvpn
#endif // OPENVPN_TUN_CLIENT_TUNBASE_H

View File

@ -0,0 +1,157 @@
#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
#define OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
#include <openvpn/tun/linux/tun.hpp>
#include <openvpn/tun/client/tunbase.hpp>
#include <openvpn/netconf/linux/route.hpp>
namespace openvpn {
namespace TunLinux {
class ClientConfig : public TunClientFactory
{
public:
typedef boost::intrusive_ptr<ClientConfig> Ptr;
std::string name;
bool ipv6;
bool tap;
int txqueuelen;
unsigned int mtu;
int n_parallel;
Frame::Ptr frame;
ProtoStats::Ptr stats;
static Ptr new_obj()
{
return new ClientConfig;
}
virtual TunClient::Ptr new_client_obj(boost::asio::io_service& io_service,
TunClientParent& parent);
private:
ClientConfig()
: ipv6(false), tap(false), txqueuelen(200), mtu(1500), n_parallel(8) {}
};
class Client : public TunClient
{
friend class ClientConfig; // calls constructor
friend class Tun<Client*>; // calls tun_read_handler
typedef Tun<Client*> TunImpl;
public:
virtual void client_start(const OptionList& opt, TransportClient& transcli)
{
if (!impl)
{
halt = false;
try {
// start tun
impl.reset(new TunImpl(io_service,
this,
config->frame,
config->stats,
config->name,
config->ipv6,
config->tap,
config->txqueuelen
));
impl->start(config->n_parallel);
// do ifconfig
impl->ifconfig(opt, config->mtu);
// add routes
route_list.reset(new RouteListLinux(opt, transcli.server_endpoint_addr()));
// signal that we are connected
parent.tun_connected();
}
catch (std::exception& e)
{
config->stats->error(ProtoStats::TUN_ERROR);
stop();
parent.tun_error(e);
}
}
}
virtual bool tun_send(BufferAllocated& buf)
{
return send(buf);
}
virtual std::string tun_name() const
{
if (impl)
return impl->name();
else
return "UNDEF_TUN";
}
virtual void stop() { stop_(); }
virtual ~Client() { stop_(); }
private:
Client(boost::asio::io_service& io_service_arg,
ClientConfig* config_arg,
TunClientParent& parent_arg)
: io_service(io_service_arg),
config(config_arg),
parent(parent_arg),
halt(false)
{
}
bool send(const Buffer& buf)
{
if (impl)
return impl->write(buf);
else
return false;
}
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
{
parent.tun_recv(pfp->buf);
}
void stop_()
{
// remove added routes
if (route_list)
{
route_list->stop();
route_list.reset();
}
// stop tun
if (impl)
{
impl->stop();
impl.reset();
}
halt = true;
}
boost::asio::io_service& io_service;
ClientConfig::Ptr config;
TunClientParent& parent;
TunImpl::Ptr impl;
RouteListLinux::Ptr route_list;
bool halt;
};
inline TunClient::Ptr ClientConfig::new_client_obj(boost::asio::io_service& io_service,
TunClientParent& parent)
{
return TunClient::Ptr(new Client(io_service, this, parent));
}
}
} // namespace openvpn
#endif // OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H

View File

@ -1,5 +1,5 @@
#ifndef OPENVPN_TUN_LINUX_TUNLINUX_H
#define OPENVPN_TUN_LINUX_TUNLINUX_H
#ifndef OPENVPN_TUN_LINUX_TUN_H
#define OPENVPN_TUN_LINUX_TUN_H
#include <sys/ioctl.h>
#include <fcntl.h>
@ -10,12 +10,11 @@
#include <string>
#include <sstream>
#include <boost/noncopyable.hpp>
#include <boost/asio.hpp>
#include <boost/weak_ptr.hpp>
#include <openvpn/common/types.hpp>
#include <openvpn/common/rc.hpp> // fixme
#include <openvpn/common/rc.hpp>
#include <openvpn/common/scoped_ptr.hpp>
#include <openvpn/common/scoped_fd.hpp>
#include <openvpn/common/dispatch.hpp>
@ -45,24 +44,24 @@ namespace openvpn {
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
template <typename ReadHandler>
class Tun : public RC<thread_unsafe_refcount> // boost::noncopyable (fixme)
class Tun : public RC<thread_unsafe_refcount>
{
public:
typedef boost::intrusive_ptr<Tun> Ptr;
Tun(boost::asio::io_service& io_service,
ReadHandler read_handler,
const Frame::Ptr& frame,
const ProtoStats::Ptr& stats,
const char *name=NULL,
const bool ipv6=false,
const bool tap=false,
const int txqueuelen=200)
ReadHandler read_handler_arg,
const Frame::Ptr& frame_arg,
const ProtoStats::Ptr& stats_arg,
const std::string name,
const bool ipv6,
const bool tap,
const int txqueuelen)
: halt_(false),
read_handler_(read_handler),
frame_(frame),
stats_(stats)
: halt(false),
read_handler(read_handler_arg),
frame(frame_arg),
stats(stats_arg)
{
static const char node[] = "/dev/net/tun";
ScopedFD fd(open(node, O_RDWR));
@ -78,10 +77,10 @@ namespace openvpn {
ifr.ifr_flags |= IFF_TAP;
else
ifr.ifr_flags |= IFF_TUN;
if (name)
if (!name.empty())
{
if (::strlen(name) < IFNAMSIZ)
::strcpy (ifr.ifr_name, name);
if (name.length() < IFNAMSIZ)
::strcpy (ifr.ifr_name, name.c_str());
else
throw tun_name_error();
}
@ -117,35 +116,48 @@ namespace openvpn {
bool write(const Buffer& buf)
{
try {
const size_t wrote = sd->write_some(buf.const_buffers_1());
stats_->inc_stat(ProtoStats::TUN_BYTES_OUT, wrote);
if (wrote == buf.size())
return true;
else
{
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
stats_->error(ProtoStats::TUN_ERROR);
return false;
}
}
catch (boost::system::system_error& e)
if (!halt)
{
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
stats_->error(ProtoStats::TUN_ERROR);
return false;
try {
const size_t wrote = sd->write_some(buf.const_buffers_1());
stats->inc_stat(ProtoStats::TUN_BYTES_OUT, wrote);
if (wrote == buf.size())
return true;
else
{
OPENVPN_LOG_TUN_ERROR("TUN partial write error");
stats->error(ProtoStats::TUN_ERROR);
return false;
}
}
catch (boost::system::system_error& e)
{
OPENVPN_LOG_TUN_ERROR("TUN write error: " << e.what());
stats->error(ProtoStats::TUN_ERROR);
return false;
}
}
else
return false;
}
void start(const int n_parallel)
{
for (int i = 0; i < n_parallel; i++)
queue_read(NULL);
if (!halt)
{
for (int i = 0; i < n_parallel; i++)
queue_read(NULL);
}
}
void stop() {
halt_ = true;
sd->close();
void stop()
{
if (!halt)
{
halt = true;
sd->close();
delete sd;
}
}
int ifconfig(const OptionList& opt, const unsigned int mtu)
@ -172,9 +184,7 @@ namespace openvpn {
}
}
~Tun() {
delete sd;
}
~Tun() { stop(); }
std::string name() const
{
@ -187,7 +197,7 @@ namespace openvpn {
OPENVPN_LOG_TUN_VERBOSE("TunLinux::queue_read");
if (!tunfrom)
tunfrom = new PacketFrom();
frame_->prepare(Frame::READ_TUN, tunfrom->buf);
frame->prepare(Frame::READ_TUN, tunfrom->buf);
sd->async_read_some(tunfrom->buf.mutable_buffers_1(),
asio_dispatch_read(&Tun::handle_read, this, tunfrom));
@ -197,18 +207,18 @@ namespace openvpn {
{
OPENVPN_LOG_TUN_VERBOSE("TunLinux::handle_read: " << error.message());
typename PacketFrom::SPtr pfp(tunfrom);
if (!halt_)
if (!halt)
{
if (!error)
{
pfp->buf.set_size(bytes_recvd);
stats_->inc_stat(ProtoStats::TUN_BYTES_IN, bytes_recvd);
read_handler_->tun_read_handler(pfp);
stats->inc_stat(ProtoStats::TUN_BYTES_IN, bytes_recvd);
read_handler->tun_read_handler(pfp);
}
else
{
OPENVPN_LOG_TUN_ERROR("TUN Read Error: " << error);
stats_->error(ProtoStats::TUN_ERROR);
stats->error(ProtoStats::TUN_ERROR);
}
queue_read(pfp.release()); // reuse buffer if still available
}
@ -216,13 +226,13 @@ namespace openvpn {
std::string name_;
boost::asio::posix::stream_descriptor *sd;
bool halt_;
ReadHandler read_handler_;
const Frame::Ptr frame_;
ProtoStats::Ptr stats_;
bool halt;
ReadHandler read_handler;
const Frame::Ptr frame;
ProtoStats::Ptr stats;
};
}
} // namespace openvpn
#endif // OPENVPN_TUN_LINUX_TUNLINUX_H
#endif // OPENVPN_TUN_LINUX_TUN_H

View File

@ -1,36 +0,0 @@
#ifndef OPENVPN_TUN_TUNBASE_H
#define OPENVPN_TUN_TUNBASE_H
#include <string>
#include <openvpn/common/rc.hpp>
namespace openvpn {
struct TunBase : public RC<thread_unsafe_refcount>
{
typedef boost::intrusive_ptr<TunBase> Ptr;
virtual void start() = 0;
virtual void stop() = 0;
virtual bool tun_send(BufferAllocated& buf) = 0;
};
struct TunParent
{
virtual void tun_recv(BufferAllocated& buf) = 0;
virtual void tun_connected() {}
virtual void tun_error(const std::string) {}
};
struct TunFactory : public RC<thread_unsafe_refcount>
{
typedef boost::intrusive_ptr<TunFactory> Ptr;
virtual TunBase::Ptr new_obj(boost::asio::io_service& io_service,
TunParent& parent) = 0;
};
} // namespace openvpn
#endif // OPENVPN_TUN_TUNBASE_H