diff --git a/src/IPStackSimulator.cc b/src/IPStackSimulator.cc index 61c55ce7..ebd2b5bf 100644 --- a/src/IPStackSimulator.cc +++ b/src/IPStackSimulator.cc @@ -135,28 +135,28 @@ IPStackSimulator::~IPStackSimulator() { } } -void IPStackSimulator::listen(const string& name, const string& socket_path, FrameInfo::LinkType link_type) { +void IPStackSimulator::listen(const string& name, const string& socket_path, Protocol proto) { int fd = ::listen(socket_path, 0, SOMAXCONN); ip_stack_simulator_log.info("Listening on Unix socket %s on fd %d as %s", socket_path.c_str(), fd, name.c_str()); - this->add_socket(name, fd, link_type); + this->add_socket(name, fd, proto); } -void IPStackSimulator::listen(const string& name, const string& addr, int port, FrameInfo::LinkType link_type) { +void IPStackSimulator::listen(const string& name, const string& addr, int port, Protocol proto) { if (port == 0) { - this->listen(name, addr, link_type); + this->listen(name, addr, proto); } else { int fd = ::listen(addr, port, SOMAXCONN); string netloc_str = render_netloc(addr, port); ip_stack_simulator_log.info("Listening on TCP interface %s on fd %d as %s", netloc_str.c_str(), fd, name.c_str()); - this->add_socket(name, fd, link_type); + this->add_socket(name, fd, proto); } } -void IPStackSimulator::listen(const string& name, int port, FrameInfo::LinkType link_type) { - this->listen(name, "", port, link_type); +void IPStackSimulator::listen(const string& name, int port, Protocol proto) { + this->listen(name, "", port, proto); } -void IPStackSimulator::add_socket(const string& name, int fd, FrameInfo::LinkType link_type) { +void IPStackSimulator::add_socket(const string& name, int fd, Protocol proto) { unique_listener l( evconnlistener_new( this->base.get(), @@ -166,7 +166,7 @@ void IPStackSimulator::add_socket(const string& name, int fd, FrameInfo::LinkTyp 0, fd), evconnlistener_free); - this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, link_type, std::move(l))); + this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, proto, std::move(l))); } uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) { @@ -180,10 +180,10 @@ uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_ad } } -IPStackSimulator::IPClient::IPClient(shared_ptr sim, FrameInfo::LinkType link_type, struct bufferevent* bev) +IPStackSimulator::IPClient::IPClient(shared_ptr sim, Protocol protocol, struct bufferevent* bev) : sim(sim), bev(bev, bufferevent_free), - link_type(link_type), + protocol(protocol), mac_addr(0), ipv4_addr(0), idle_timeout_event(event_new(sim->base.get(), -1, EV_TIMEOUT, &IPStackSimulator::IPClient::dispatch_on_idle_timeout, this), event_free) { @@ -256,7 +256,7 @@ void IPStackSimulator::on_listen_accept(struct evconnlistener* listener, struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - auto c = make_shared(this->shared_from_this(), listening_socket->link_type, bev); + auto c = make_shared(this->shared_from_this(), listening_socket->protocol, bev); this->bev_to_client.emplace(make_pair(bev, c)); bufferevent_setcb(bev, &IPStackSimulator::dispatch_on_client_input, nullptr, @@ -300,24 +300,64 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) { struct timeval tv = usecs_to_timeval(idle_timeout_usecs); event_add(c->idle_timeout_event.get(), &tv); - while (evbuffer_get_length(buf) >= 2) { - uint16_t frame_size; - evbuffer_copyout(buf, &frame_size, 2); - if (evbuffer_get_length(buf) < static_cast(frame_size + 2)) { - break; // No complete frame available; done for now - } + switch (c->protocol) { + case Protocol::ETHERNET_TAPSERVER: + case Protocol::HDLC_TAPSERVER: + while (evbuffer_get_length(buf) >= 2) { + uint16_t frame_size; + evbuffer_copyout(buf, &frame_size, 2); + if (evbuffer_get_length(buf) < static_cast(frame_size + 2)) { + break; // No complete frame available; done for now + } - evbuffer_drain(buf, 2); - string frame(frame_size, '\0'); - evbuffer_remove(buf, frame.data(), frame.size()); + evbuffer_drain(buf, 2); + string frame(frame_size, '\0'); + evbuffer_remove(buf, frame.data(), frame.size()); - try { - this->on_client_frame(c, frame); - } catch (const exception& e) { - if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { - print_data(stderr, frame); + try { + this->on_client_frame(c, frame); + } catch (const exception& e) { + if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { + print_data(stderr, frame); + } + } } - } + break; + case Protocol::HDLC_RAW: + while (evbuffer_get_length(buf) >= 2) { + struct evbuffer_ptr res = evbuffer_search(buf, "\x7E", 1, nullptr); + if (res.pos < 0) { + break; + } + size_t start_offset = res.pos; + + if (evbuffer_ptr_set(buf, &res, 1, EVBUFFER_PTR_ADD)) { + ip_stack_simulator_log.warning("Cannot advance search for end of frame"); + break; + } + + struct evbuffer_ptr end_res = evbuffer_search(buf, "\x7E", 1, &res); + if (end_res.pos < 0) { + break; + } + size_t frame_size = end_res.pos + 1 - start_offset; + + if (start_offset) { + evbuffer_drain(buf, start_offset); + } + + string frame(frame_size, '\0'); + evbuffer_remove(buf, frame.data(), frame.size()); + + try { + this->on_client_frame(c, frame); + } catch (const exception& e) { + if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { + print_data(stderr, frame); + } + } + } + break; } } @@ -343,8 +383,8 @@ void IPStackSimulator::send_layer3_frame(shared_ptr c, FrameInfo::Prot void IPStackSimulator::send_layer3_frame(shared_ptr c, FrameInfo::Protocol proto, const void* data, size_t size) const { struct evbuffer* out_buf = bufferevent_get_output(c->bev.get()); - switch (c->link_type) { - case FrameInfo::LinkType::ETHERNET: { + switch (c->protocol) { + case Protocol::ETHERNET_TAPSERVER: { EthernetHeader ether; ether.dest_mac = c->mac_addr; ether.src_mac = this->host_mac_address_bytes; @@ -376,7 +416,8 @@ void IPStackSimulator::send_layer3_frame(shared_ptr c, FrameInfo::Prot break; } - case FrameInfo::LinkType::HDLC: { + case Protocol::HDLC_TAPSERVER: + case Protocol::HDLC_RAW: { HDLCHeader hdlc; hdlc.start_sentinel1 = 0x7E; hdlc.address = 0xFF; @@ -413,8 +454,10 @@ void IPStackSimulator::send_layer3_frame(shared_ptr c, FrameInfo::Prot print_data(stderr, w.str()); } - le_uint16_t frame_size = escaped.size(); - evbuffer_add(out_buf, &frame_size, 2); + if (c->protocol == Protocol::HDLC_TAPSERVER) { + le_uint16_t frame_size = escaped.size(); + evbuffer_add(out_buf, &frame_size, 2); + } evbuffer_add(out_buf, escaped.data(), escaped.size()); if (this->pcap_text_log_file) { this->log_frame(escaped); @@ -428,9 +471,13 @@ void IPStackSimulator::send_layer3_frame(shared_ptr c, FrameInfo::Prot } void IPStackSimulator::on_client_frame(shared_ptr c, const string& frame) { + FrameInfo::LinkType link_type = (c->protocol == Protocol::ETHERNET_TAPSERVER) + ? FrameInfo::LinkType::ETHERNET + : FrameInfo::LinkType::HDLC; + const string* effective_data = &frame; string hdlc_unescaped_data; - if (c->link_type == FrameInfo::LinkType::HDLC) { + if (link_type == FrameInfo::LinkType::HDLC) { hdlc_unescaped_data = unescape_hdlc_frame(frame); effective_data = &hdlc_unescaped_data; } @@ -439,7 +486,7 @@ void IPStackSimulator::on_client_frame(shared_ptr c, const string& fra } this->log_frame(*effective_data); - FrameInfo fi(c->link_type, *effective_data); + FrameInfo fi(link_type, *effective_data); if (ip_stack_simulator_log.should_log(LogLevel::DEBUG)) { string fi_header = fi.header_str(); ip_stack_simulator_log.debug("Frame header: %s", fi_header.c_str()); @@ -1314,7 +1361,7 @@ void IPStackSimulator::send_pending_push_frame(shared_ptr c, IPClient: } size_t bytes_to_send = min(pending_bytes, conn.next_push_max_frame_size); - if ((c->link_type == FrameInfo::LinkType::HDLC) && (bytes_to_send > 200)) { + if ((c->protocol == Protocol::HDLC_TAPSERVER) && (bytes_to_send > 200)) { // There is a bug in Dolphin's modem implementation (which I wrote, so it's // my fault) that causes commands to be dropped when too much data is sent // at once. To work around this, we only send up to 200 bytes in each push diff --git a/src/IPStackSimulator.hh b/src/IPStackSimulator.hh index 66bb37a4..376475c5 100644 --- a/src/IPStackSimulator.hh +++ b/src/IPStackSimulator.hh @@ -16,15 +16,21 @@ class IPStackSimulator : public std::enable_shared_from_this { public: + enum class Protocol { + ETHERNET_TAPSERVER = 0, + HDLC_TAPSERVER, + HDLC_RAW, + }; + IPStackSimulator( std::shared_ptr base, std::shared_ptr state); ~IPStackSimulator(); - void listen(const std::string& name, const std::string& socket_path, FrameInfo::LinkType link_type); - void listen(const std::string& name, const std::string& addr, int port, FrameInfo::LinkType link_type); - void listen(const std::string& name, int port, FrameInfo::LinkType link_type); - void add_socket(const std::string& name, int fd, FrameInfo::LinkType link_type); + void listen(const std::string& name, const std::string& socket_path, Protocol protocol); + void listen(const std::string& name, const std::string& addr, int port, Protocol protocol); + void listen(const std::string& name, int port, Protocol protocol); + void add_socket(const std::string& name, int fd, Protocol protocol); static uint32_t connect_address_for_remote_address(uint32_t remote_addr); @@ -41,7 +47,7 @@ private: std::weak_ptr sim; unique_bufferevent bev; - FrameInfo::LinkType link_type; + Protocol protocol; uint32_t hdlc_escape_control_character_flags = 0xFFFFFFFF; uint32_t hdlc_remote_magic_number = 0; parray mac_addr; // Only used for LinkType::ETHERNET @@ -80,7 +86,7 @@ private: unique_event idle_timeout_event; - IPClient(std::shared_ptr sim, FrameInfo::LinkType link_type, struct bufferevent* bev); + IPClient(std::shared_ptr sim, Protocol protocol, struct bufferevent* bev); static void dispatch_on_idle_timeout(evutil_socket_t fd, short events, void* ctx); void on_idle_timeout(); @@ -88,12 +94,12 @@ private: struct ListeningSocket { std::string name; - FrameInfo::LinkType link_type; + Protocol protocol; unique_listener listener; - ListeningSocket(const std::string& name, FrameInfo::LinkType link_type, unique_listener&& l) + ListeningSocket(const std::string& name, Protocol protocol, unique_listener&& l) : name(name), - link_type(link_type), + protocol(protocol), listener(std::move(l)) {} }; diff --git a/src/Main.cc b/src/Main.cc index 6b4068a9..4dce82d9 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2452,12 +2452,17 @@ Action a_run_server_replay_log( for (const auto& it : state->ip_stack_addresses) { auto netloc = parse_netloc(it); string spec = (netloc.second == 0) ? ("T-IPS-" + netloc.first) : string_printf("T-IPS-%hu", netloc.second); - ip_stack_simulator->listen(spec, netloc.first, netloc.second, FrameInfo::LinkType::ETHERNET); + ip_stack_simulator->listen(spec, netloc.first, netloc.second, IPStackSimulator::Protocol::ETHERNET_TAPSERVER); } for (const auto& it : state->ppp_stack_addresses) { auto netloc = parse_netloc(it); - string spec = (netloc.second == 0) ? ("T-PPPS-" + netloc.first) : string_printf("T-PPPS-%hu", netloc.second); - ip_stack_simulator->listen(spec, netloc.first, netloc.second, FrameInfo::LinkType::HDLC); + string spec = (netloc.second == 0) ? ("T-PPPST-" + netloc.first) : string_printf("T-PPPST-%hu", netloc.second); + ip_stack_simulator->listen(spec, netloc.first, netloc.second, IPStackSimulator::Protocol::HDLC_TAPSERVER); + } + for (const auto& it : state->ppp_raw_addresses) { + auto netloc = parse_netloc(it); + string spec = (netloc.second == 0) ? ("T-PPPSR-" + netloc.first) : string_printf("T-PPPSR-%hu", netloc.second); + ip_stack_simulator->listen(spec, netloc.first, netloc.second, IPStackSimulator::Protocol::HDLC_RAW); } } diff --git a/src/ServerState.cc b/src/ServerState.cc index 4a1a3fc0..3e4d2529 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -622,6 +622,16 @@ void ServerState::load_config_early() { } } catch (const out_of_range&) { } + try { + for (const auto& item : this->config_json.at("PPPRawListen").as_list()) { + if (item->is_int()) { + this->ppp_raw_addresses.emplace_back(string_printf("0.0.0.0:%" PRId64, item->as_int())); + } else { + this->ppp_raw_addresses.emplace_back(item->as_string()); + } + } + } catch (const out_of_range&) { + } try { for (const auto& item : this->config_json.at("HTTPListen").as_list()) { if (item->is_int()) { diff --git a/src/ServerState.hh b/src/ServerState.hh index 09f9b061..4e75024c 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -79,6 +79,7 @@ struct ServerState : public std::enable_shared_from_this { uint16_t dns_server_port = 0; std::vector ip_stack_addresses; std::vector ppp_stack_addresses; + std::vector ppp_raw_addresses; std::vector http_addresses; uint64_t client_ping_interval_usecs = 30000000; uint64_t client_idle_timeout_usecs = 60000000;