diff --git a/tools/socktap/CMakeLists.txt b/tools/socktap/CMakeLists.txt index 848583b7f..5b1fc1ce5 100644 --- a/tools/socktap/CMakeLists.txt +++ b/tools/socktap/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(socktap raw_socket_link.cpp router_context.cpp security.cpp + tcp_link.cpp time_trigger.cpp udp_link.cpp ) diff --git a/tools/socktap/link_layer.cpp b/tools/socktap/link_layer.cpp index 54e3a4e8b..01ce1f236 100644 --- a/tools/socktap/link_layer.cpp +++ b/tools/socktap/link_layer.cpp @@ -1,5 +1,6 @@ #include "link_layer.hpp" #include "raw_socket_link.hpp" +#include "tcp_link.hpp" #include "udp_link.hpp" #include #include @@ -9,7 +10,7 @@ #endif std::unique_ptr -create_link_layer(boost::asio::io_service& io_service, const EthernetDevice& device, const std::string& name) +create_link_layer(boost::asio::io_service& io_service, const EthernetDevice& device, const std::string& name, const boost::program_options::variables_map& vm) { std::unique_ptr link_layer; @@ -29,7 +30,43 @@ create_link_layer(boost::asio::io_service& io_service, const EthernetDevice& dev namespace ip = boost::asio::ip; ip::udp::endpoint multicast(ip::address::from_string("239.118.122.97"), 8947); link_layer.reset(new UdpLink { io_service, multicast }); + } else if (name.substr(0, 3) == "tcp") { + namespace ip = boost::asio::ip; + + TcpLink* tcp = new TcpLink { io_service }; + + std::string tcp_ip; + unsigned short tcp_port, tcp_ip_len; + + if (vm.count("tcp-connect")) { + for (const std::string& ip_port : vm["tcp-connect"].as>()) { + tcp_ip_len = ip_port.find(":"); + tcp_ip = ip_port.substr(0, tcp_ip_len); + tcp_port = std::stoi(ip_port.substr(tcp_ip_len + 1)); + tcp->connect({ip::address::from_string(tcp_ip), tcp_port}); + } + } + + if (vm.count("tcp-accept")) { + for (const std::string& ip_port : vm["tcp-accept"].as>()) { + tcp_ip_len = ip_port.find(":"); + tcp_ip = ip_port.substr(0, tcp_ip_len); + tcp_port = std::stoi(ip_port.substr(tcp_ip_len + 1)); + tcp->accept({ip::address::from_string(tcp_ip), tcp_port}); + } + } + + link_layer.reset(tcp); + } return link_layer; } + +void add_link_layer_options(boost::program_options::options_description& options) +{ + options.add_options() + ("tcp-connect", boost::program_options::value>()->multitoken(), "Connect to TCP-Host(s). Comma separated list of [ip]:[port].") + ("tcp-accept", boost::program_options::value>()->multitoken(), "Accept TCP-Connections. Comma separated list of [ip]:[port].") + ; +} diff --git a/tools/socktap/link_layer.hpp b/tools/socktap/link_layer.hpp index 6efa31006..4790300b0 100644 --- a/tools/socktap/link_layer.hpp +++ b/tools/socktap/link_layer.hpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -23,7 +25,9 @@ class LinkLayer : public vanetza::access::Interface, public LinkLayerIndication }; std::unique_ptr -create_link_layer(boost::asio::io_service&, const EthernetDevice&, const std::string& name); +create_link_layer(boost::asio::io_service&, const EthernetDevice&, const std::string& name, const boost::program_options::variables_map& vm); + +void add_link_layer_options(boost::program_options::options_description&); #endif /* LINK_LAYER_HPP_FGEK0QTH */ diff --git a/tools/socktap/main.cpp b/tools/socktap/main.cpp index b204fe489..de61b8716 100644 --- a/tools/socktap/main.cpp +++ b/tools/socktap/main.cpp @@ -36,6 +36,7 @@ int main(int argc, const char** argv) ; add_positioning_options(options); add_security_options(options); + add_link_layer_options(options); po::positional_options_description positional_options; positional_options.add("interface", 1); @@ -80,7 +81,7 @@ int main(int argc, const char** argv) } const std::string link_layer_name = vm["link-layer"].as(); - auto link_layer = create_link_layer(io_service, device, link_layer_name); + auto link_layer = create_link_layer(io_service, device, link_layer_name, vm); if (!link_layer) { std::cerr << "No link layer '" << link_layer_name << "' found." << std::endl; return 1; diff --git a/tools/socktap/tcp_link.cpp b/tools/socktap/tcp_link.cpp new file mode 100644 index 000000000..ff43692ae --- /dev/null +++ b/tools/socktap/tcp_link.cpp @@ -0,0 +1,169 @@ +#include "tcp_link.hpp" +#include +#include +#include +#include +#include + +namespace ip = boost::asio::ip; +using namespace vanetza; + +TcpLink::TcpLink(boost::asio::io_service& io_service) : + io_service_(&io_service) +{ + +} + +TcpLink::~TcpLink() +{ + for (auto &ts : sockets_) { + delete ts; + } + for (auto &acceptor : acceptors_) { + delete acceptor.second; + } +} + +void TcpLink::indicate(IndicationCallback cb) +{ + callback_ = cb; +} + +void TcpLink::request(const access::DataRequest& request, std::unique_ptr packet) +{ + packet->layer(OsiLayer::Link) = create_ethernet_header(request.destination_addr, request.source_addr, request.ether_type); + + std::array const_buffers; + for (auto& layer : osi_layer_range()) { + const auto index = distance(OsiLayer::Link, layer); + packet->layer(layer).convert(tx_buffers_[index]); + const_buffers[index] = boost::asio::buffer(tx_buffers_[index]); + } + + std::list::iterator i = sockets_.begin(); + + while (i != sockets_.end()) { + if ((*i)->connected()) { + (*i)->request(const_buffers); + i++; + } else { + sockets_.erase(i++); + std::cerr << "Socket removed" << std::endl; + } + } +} + +void TcpLink::connect(ip::tcp::endpoint ep) +{ + TcpSocket* ts = new TcpSocket(*io_service_, callback_); + ts->connect(ep); + sockets_.push_back(ts); +} + +void TcpLink::accept(ip::tcp::endpoint ep) +{ + if (!acceptors_[ep]) { + acceptors_[ep] = new ip::tcp::acceptor(*io_service_, ep); + } + + TcpSocket* ts = new TcpSocket(*io_service_, callback_); + boost::system::error_code ec; + + acceptors_[ep]->async_accept( + ts->socket(), + boost::bind( + &TcpLink::accept_handler, + this, + ec, + ts, + ep + ) + ); + +} + +void TcpLink::accept_handler(boost::system::error_code& ec, TcpSocket* ts, ip::tcp::endpoint ep) +{ + sockets_.push_back(ts); + ts->do_receive(); + ts->connected(true); + accept(ep); +} + + +/** + * TcpSocket + */ + +TcpSocket::TcpSocket(boost::asio::io_service& io_service, IndicationCallback& cb) : + socket_(io_service), + callback_(&cb), + rx_buffer_(2560, 0x00) +{ + +} + +void TcpSocket::connect(ip::tcp::endpoint ep) +{ + boost::system::error_code ec; + socket_.connect(ep, ec); + if (!ec) { + is_connected_ = true; + do_receive(); + } + +} + +void TcpSocket::request(std::array const_buffers) +{ + boost::system::error_code ec; + socket_.write_some(const_buffers, ec); + + if (ec) { + is_connected_ = false; + } +} + +void TcpSocket::do_receive() +{ + socket_.async_read_some(boost::asio::buffer(rx_buffer_), + [this](boost::system::error_code ec, std::size_t length) { + if (!ec) { + is_connected_ = true; + ByteBuffer buffer(rx_buffer_.begin(), rx_buffer_.begin() + length); + CohesivePacket packet(std::move(buffer), OsiLayer::Link); + if (packet.size(OsiLayer::Link) < EthernetHeader::length_bytes) { + std::cerr << "Dropped TCP packet too short to contain Ethernet header\n"; + } else { + packet.set_boundary(OsiLayer::Link, EthernetHeader::length_bytes); + auto link_range = packet[OsiLayer::Link]; + EthernetHeader eth = decode_ethernet_header(link_range.begin(), link_range.end()); + if (callback_) { + (*callback_)(std::move(packet), eth); + } + } + do_receive(); + } else { + is_connected_ = false; + } + }); + +} + +ip::tcp::socket& TcpSocket::socket() +{ + return socket_; +} + +bool TcpSocket::connected() +{ + return is_connected_; +} + +void TcpSocket::connected(bool b) +{ + is_connected_ = b; +} + + + diff --git a/tools/socktap/tcp_link.hpp b/tools/socktap/tcp_link.hpp new file mode 100644 index 000000000..a07052fed --- /dev/null +++ b/tools/socktap/tcp_link.hpp @@ -0,0 +1,64 @@ +#ifndef TCP_LINK_HPP_A16QFBX3 +#define TCP_LINK_HPP_A16QFBX3 + +#include "link_layer.hpp" +#include +#include +#include +#include +#include + +static constexpr std::size_t layers_ = num_osi_layers(vanetza::OsiLayer::Link, vanetza::OsiLayer::Application); + + +class TcpSocket +{ +public: + using IndicationCallback = std::function; + + TcpSocket(boost::asio::io_service&, IndicationCallback&); + + void connect(boost::asio::ip::tcp::endpoint); + void request(std::array); + void do_receive(); + + boost::asio::ip::tcp::socket& socket(); + bool connected(); + void connected(bool); + +private: + + bool is_connected_; + boost::asio::io_service* io_service_; + boost::asio::ip::tcp::endpoint endpoint_; + boost::asio::ip::tcp::socket socket_; + vanetza::ByteBuffer rx_buffer_; + IndicationCallback* callback_; + +}; + + +class TcpLink : public LinkLayer +{ +public: + TcpLink(boost::asio::io_service&); + ~TcpLink(); + + void indicate(IndicationCallback) override; + void request(const vanetza::access::DataRequest&, std::unique_ptr) override; + void connect(boost::asio::ip::tcp::endpoint); + void accept(boost::asio::ip::tcp::endpoint); + void accept_handler(boost::system::error_code& ec, TcpSocket* ts, boost::asio::ip::tcp::endpoint ep); + +private: + std::list sockets_; + std::map acceptors_; + IndicationCallback callback_; + boost::asio::io_service* io_service_; + std::array tx_buffers_; +}; + + + +#endif /* TCP_LINK_HPP_A16QFBX3 */ +