implement HDLC/PPP in IPStackSimulator

This commit is contained in:
Martin Michelsen
2023-12-03 00:05:20 -08:00
parent 0442f6e579
commit e49e159eee
9 changed files with 757 additions and 251 deletions
+137 -100
View File
@@ -13,120 +13,130 @@ static inline uint16_t collapse_checksum(uint32_t sum) {
return (sum & 0xFFFF) + (sum >> 16);
}
FrameInfo::FrameInfo()
: ether(nullptr),
ether_protocol(0),
ipv4(nullptr),
arp(nullptr),
udp(nullptr),
tcp(nullptr),
header_start(nullptr),
payload(nullptr),
total_size(0),
tcp_options_size(0),
payload_size(0) {}
FrameInfo::FrameInfo(LinkType link_type, const string& data)
: FrameInfo(link_type, data.data(), data.size()) {}
FrameInfo::FrameInfo(const string& data) : FrameInfo(data.data(), data.size()) {}
FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
: FrameInfo() {
this->link_type = link_type;
this->header_start = header_start;
this->total_size = size;
this->payload_size = size;
FrameInfo::FrameInfo(const void* header_start, size_t size)
: ether(nullptr),
ether_protocol(0),
ipv4(nullptr),
arp(nullptr),
udp(nullptr),
tcp(nullptr),
header_start(header_start),
payload(nullptr),
total_size(size),
tcp_options_size(0),
payload_size(size) {
StringReader r(header_start, size);
// Parse ethernet header
if (this->payload_size < sizeof(EthernetHeader)) {
throw invalid_argument("frame is too small for ethernet");
}
this->payload_size -= sizeof(EthernetHeader);
this->ether = reinterpret_cast<const EthernetHeader*>(header_start);
this->ether_protocol = this->ether->protocol;
// Parse link-layer header
Protocol proto = Protocol::NONE;
switch (this->link_type) {
case LinkType::ETHERNET:
this->payload_size -= sizeof(EthernetHeader);
this->ether = &r.get<EthernetHeader>();
this->ether_protocol = this->ether->protocol;
// Unwrap VLAN tags if necessary
while ((this->ether_protocol == 0x8100) || (this->ether_protocol == 0x88A8)) {
r.skip(2);
this->ether_protocol = r.get_u16b();
this->payload_size -= 4;
}
switch (this->ether_protocol) {
case 0x0800:
proto = Protocol::IPV4;
break;
case 0x0806:
proto = Protocol::ARP;
break;
}
break;
// Figure out the protocol
const be_uint16_t* u16data = reinterpret_cast<const be_uint16_t*>(this->ether + 1);
while ((this->ether_protocol == 0x8100) || (this->ether_protocol == 0x88A8)) {
if (this->payload_size < 4) {
throw invalid_argument("VLAN tags exceed frame size");
}
this->ether_protocol = u16data[1];
u16data += 2;
this->payload_size -= 4;
case LinkType::HDLC:
this->payload_size -= (sizeof(HDLCHeader) + 3); // Trim off checksum and end sentinel
this->hdlc = &r.get<HDLCHeader>();
this->hdlc_checksum = r.pget_u16b(r.where() + this->payload_size);
switch (this->hdlc->protocol) {
case 0xC021:
proto = Protocol::LCP;
break;
case 0xC023:
proto = Protocol::PAP;
break;
case 0x8021:
proto = Protocol::IPCP;
break;
case 0x0021:
proto = Protocol::IPV4;
break;
}
break;
default:
throw logic_error("invalid link type");
}
// TODO: Some less-common protocols that we might want to support:
// 0x8035 = RARP
// 0x809B = AppleTalk
// 0x80F3 = AppleTalk ARP
// 0x8137 = IPX
// 0x9000 = loopback
// Parse protocol headers if possible
if (this->ether_protocol == 0x0800) { // IPv4
if (this->payload_size < sizeof(IPv4Header)) {
throw invalid_argument("frame is too small for ipv4 header");
}
this->ipv4 = reinterpret_cast<const IPv4Header*>(u16data);
if (this->payload_size < this->ipv4->size) {
throw invalid_argument("ipv4 header specifies size larger than frame");
}
this->payload_size = this->ipv4->size - sizeof(IPv4Header);
if (this->ipv4->protocol == 0x06) {
if (this->payload_size < sizeof(TCPHeader)) {
throw invalid_argument("frame is too small for tcp4 header");
// Parse inner protocol headers
switch (proto) {
case Protocol::NONE:
throw runtime_error("unknown protocol");
case Protocol::LCP:
this->payload_size -= sizeof(LCPHeader);
this->lcp = &r.get<LCPHeader>();
break;
case Protocol::PAP:
this->payload_size -= sizeof(PAPHeader);
this->pap = &r.get<PAPHeader>();
break;
case Protocol::IPCP:
this->payload_size -= sizeof(IPCPHeader);
this->ipcp = &r.get<IPCPHeader>();
break;
case Protocol::IPV4:
this->ipv4 = &r.get<IPv4Header>();
if (this->payload_size < this->ipv4->size) {
throw invalid_argument("ipv4 header specifies size larger than frame");
}
this->tcp = reinterpret_cast<const TCPHeader*>(this->ipv4 + 1);
size_t tcp_header_size = (this->tcp->flags >> 12) * 4;
if (tcp_header_size < sizeof(TCPHeader) || tcp_header_size > this->payload_size) {
throw invalid_argument("frame is too small for tcp4 header with options");
this->payload_size = this->ipv4->size - sizeof(IPv4Header);
if (this->ipv4->protocol == 0x06) {
this->tcp = &r.get<TCPHeader>();
size_t tcp_header_size = (this->tcp->flags >> 12) * 4;
if (tcp_header_size < sizeof(TCPHeader) || tcp_header_size > this->payload_size) {
throw invalid_argument("frame is too small for tcp4 header with options");
}
this->tcp_options_size = tcp_header_size - sizeof(TCPHeader);
this->payload_size -= tcp_header_size;
r.skip(tcp_header_size - sizeof(TCPHeader));
} else if (this->ipv4->protocol == 0x11) {
this->payload_size -= sizeof(UDPHeader);
this->udp = &r.get<UDPHeader>();
}
this->tcp_options_size = tcp_header_size - sizeof(TCPHeader);
this->payload_size -= tcp_header_size;
this->payload = reinterpret_cast<const uint8_t*>(this->tcp) + tcp_header_size;
} else if (this->ipv4->protocol == 0x11) {
if (this->payload_size < sizeof(UDPHeader)) {
throw invalid_argument("frame is too small for udp4 header");
}
this->payload_size -= sizeof(UDPHeader);
this->udp = reinterpret_cast<const UDPHeader*>(this->ipv4 + 1);
this->payload = this->udp + 1;
} else {
this->payload = this->ipv4 + 1;
}
} else if (this->ether_protocol == 0x0806) { // ARP
if (this->payload_size < sizeof(const ARPHeader)) {
throw invalid_argument("frame is too small for arp header");
}
this->payload_size -= sizeof(ARPHeader);
this->arp = reinterpret_cast<const ARPHeader*>(u16data);
this->payload = this->arp + 1;
} else {
throw runtime_error("unknown protocol");
break;
case Protocol::ARP:
this->payload_size -= sizeof(ARPHeader);
this->arp = &r.get<ARPHeader>();
break;
}
this->payload = r.getv(this->payload_size);
}
string FrameInfo::header_str() const {
if (!this->ether) {
if (!this->ether && !this->hdlc) {
return "<invalid-frame-info>";
}
string ret = string_printf(
"%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX->%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
this->ether->src_mac[0], this->ether->src_mac[1], this->ether->src_mac[2],
this->ether->src_mac[3], this->ether->src_mac[4], this->ether->src_mac[5],
this->ether->dest_mac[0], this->ether->dest_mac[1], this->ether->dest_mac[2],
this->ether->dest_mac[3], this->ether->dest_mac[4], this->ether->dest_mac[5]);
string ret;
if (this->ether) {
ret = string_printf(
"ETHER:%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX->%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
this->ether->src_mac[0], this->ether->src_mac[1], this->ether->src_mac[2],
this->ether->src_mac[3], this->ether->src_mac[4], this->ether->src_mac[5],
this->ether->dest_mac[0], this->ether->dest_mac[1], this->ether->dest_mac[2],
this->ether->dest_mac[3], this->ether->dest_mac[4], this->ether->dest_mac[5]);
} else if (this->hdlc) {
ret = string_printf("HDLC:%02hhX/%02hhX", this->hdlc->address, this->hdlc->control);
} else {
return "<invalid-frame-info>";
}
if (this->arp) {
ret += string_printf(
@@ -169,7 +179,11 @@ string FrameInfo::header_str() const {
}
} else {
ret += string_printf(",proto=%04hX", this->ether->protocol.load());
if (this->ether) {
ret += string_printf(",proto=%04hX", this->ether->protocol.load());
} else if (this->hdlc) {
ret += string_printf(",proto=%04hX", this->hdlc->protocol.load());
}
}
return ret;
@@ -292,3 +306,26 @@ uint16_t FrameInfo::computed_tcp4_checksum() const {
*this->ipv4, *this->tcp, this->tcp + 1,
this->payload_size + this->tcp_options_size);
}
uint16_t FrameInfo::computed_hdlc_checksum(const void* vdata, size_t size) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(vdata);
uint16_t crc = 0xFFFF;
for (size_t z = 0; z < size; z++) {
crc ^= data[z];
for (size_t b = 0; b < 8; b++) {
crc = (crc & 1) ? ((crc >> 1) ^ 0x8408) : (crc >> 1);
}
}
return ~crc;
}
uint16_t FrameInfo::computed_hdlc_checksum() const {
if (!this->hdlc) {
throw logic_error("cannot compute HDLC checksum for non-HDLC frame");
}
return this->computed_hdlc_checksum(&this->hdlc->address, this->total_size - 4);
}
uint16_t FrameInfo::stored_hdlc_checksum() const {
return *reinterpret_cast<const le_uint16_t*>(reinterpret_cast<const uint8_t*>(this->header_start) + (this->total_size - 3));
}
+80 -19
View File
@@ -3,9 +3,35 @@
#include <stdint.h>
#include <phosg/Encoding.hh>
#include <phosg/Strings.hh>
#include "Text.hh"
struct HDLCHeader {
uint8_t start_sentinel1; // 0x7E
uint8_t address; // 0xFF usually
uint8_t control; // 0x03 for PPP
be_uint16_t protocol;
} __attribute__((packed));
struct LCPHeader {
uint8_t command;
uint8_t request_id;
be_uint16_t size;
} __attribute__((packed));
struct PAPHeader {
uint8_t command;
uint8_t request_id;
be_uint16_t size;
} __attribute__((packed));
struct IPCPHeader {
uint8_t command;
uint8_t request_id;
be_uint16_t size;
} __attribute__((packed));
struct EthernetHeader {
parray<uint8_t, 6> dest_mac;
parray<uint8_t, 6> src_mac;
@@ -82,40 +108,75 @@ struct DHCPHeader {
} __attribute__((packed));
struct FrameInfo {
// This is always valid
const EthernetHeader* ether;
uint16_t ether_protocol;
enum class LinkType {
ETHERNET = 0,
HDLC,
};
enum class Protocol {
NONE = 0,
LCP,
PAP,
IPCP,
IPV4,
ARP,
// TODO: Some less-common protocols that we might want to support:
// Ether / HDLC = proto
// 0x8035 / ?????? = RARP
// 0x809B / 0x0029 = AppleTalk
// 0x80F3 / ?????? = AppleTalk ARP
// 0x8137 / 0x002B = IPX
};
LinkType link_type = LinkType::ETHERNET;
// Exactly one of these headers is valid
const EthernetHeader* ether = nullptr;
uint16_t ether_protocol = 0;
const HDLCHeader* hdlc = nullptr;
uint16_t hdlc_checksum = 0;
// One of these may be non-null if hdlc is valid
const LCPHeader* lcp = nullptr;
const PAPHeader* pap = nullptr;
const IPCPHeader* ipcp = nullptr;
// At most one of these is not null
const IPv4Header* ipv4;
const ARPHeader* arp;
const IPv4Header* ipv4 = nullptr;
const ARPHeader* arp = nullptr;
// One of these may be not null if this->ipv4 is not null
const UDPHeader* udp;
const TCPHeader* tcp;
const UDPHeader* udp = nullptr;
const TCPHeader* tcp = nullptr;
const void* header_start;
const void* payload;
size_t total_size;
size_t tcp_options_size;
size_t payload_size;
const void* header_start = nullptr;
const void* payload = nullptr;
size_t total_size = 0;
size_t tcp_options_size = 0;
size_t payload_size = 0;
FrameInfo();
FrameInfo(const std::string& data);
FrameInfo(const void* data, size_t size);
FrameInfo() = default;
FrameInfo(LinkType link_type, const std::string& data);
FrameInfo(LinkType link_type, const void* data, size_t size);
std::string header_str() const;
inline StringReader read_payload() const {
return StringReader(this->payload, this->payload_size);
}
void truncate(size_t new_total_size);
size_t size_from_header() const;
static uint16_t computed_ipv4_header_checksum(const IPv4Header& ipv4);
uint16_t computed_ipv4_header_checksum() const;
static uint16_t computed_udp4_checksum(
const IPv4Header& ipv4, const UDPHeader& udp, const void* data, size_t size);
static uint16_t computed_udp4_checksum(const IPv4Header& ipv4, const UDPHeader& udp, const void* data, size_t size);
uint16_t computed_udp4_checksum() const;
static uint16_t computed_tcp4_checksum(
const IPv4Header& ip, const TCPHeader& tcp, const void* data, size_t size);
static uint16_t computed_tcp4_checksum(const IPv4Header& ip, const TCPHeader& tcp, const void* data, size_t size);
uint16_t computed_tcp4_checksum() const;
static uint16_t computed_hdlc_checksum(const void* data, size_t size);
uint16_t computed_hdlc_checksum() const;
uint16_t stored_hdlc_checksum() const;
};
+494 -115
View File
@@ -21,6 +21,65 @@ using namespace std;
static const size_t DEFAULT_RESEND_PUSH_USECS = 200000; // 200ms
static string unescape_hdlc_frame(const void* data, size_t size) {
StringReader r(data, size);
if (r.get_u8(data) != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
}
string ret("\x7E", 1);
while (r.get_u8(false) != 0x7E) {
uint8_t ch = r.get_u8();
if (ch == 0x7D) {
ch = r.get_u8();
if (ch == 0x7E) {
throw runtime_error("abort sequence received");
}
ret.push_back(ch ^ 0x20);
} else {
ret.push_back(ch);
}
}
ret.push_back(0x7E);
return ret;
}
static string unescape_hdlc_frame(const string& data) {
return unescape_hdlc_frame(data.data(), data.size());
}
static string escape_hdlc_frame(const void* data, size_t size, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
if (size < 2) {
throw runtime_error("HDLC frame too small for start and end sentinels");
}
StringReader r(data, size);
if (r.pget_u8(size - 1) != 0x7E) {
throw runtime_error("HDLC frame does not end with 7E");
}
r.truncate(size - 1);
if (r.get_u8() != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
}
string ret("\x7E", 1);
while (!r.eof()) {
uint8_t ch = r.get_u8();
if ((ch == 0x7D) || (ch == 0x7E) || ((ch < 0x20) && ((escape_control_character_flags >> ch) & 1))) {
ret.push_back(0x7D);
ret.push_back(ch ^ 0x20);
} else {
ret.push_back(ch);
}
}
ret.push_back(0x7E);
return ret;
}
static string escape_hdlc_frame(const string& data, uint32_t escape_control_character_flags = 0xFFFFFFFF) {
return escape_hdlc_frame(data.data(), data.size(), escape_control_character_flags);
}
// Note: these functions exist because seq nums are allowed to wrap around the
// 32-bit integer space by design. We have to do the subtraction before the
// comparison to allow integer overflow to occur if needed.
@@ -51,8 +110,7 @@ string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
}
}
string IPStackSimulator::str_for_tcp_connection(shared_ptr<const IPClient> c,
const IPClient::TCPConnection& conn) {
string IPStackSimulator::str_for_tcp_connection(shared_ptr<const IPClient> c, const IPClient::TCPConnection& conn) {
uint64_t key = IPStackSimulator::tcp_conn_key_for_connection(conn);
string server_netloc_str = str_for_ipv4_netloc(conn.server_addr, conn.server_port);
string client_netloc_str = str_for_ipv4_netloc(c->ipv4_addr, conn.client_port);
@@ -77,28 +135,28 @@ IPStackSimulator::~IPStackSimulator() {
}
}
void IPStackSimulator::listen(const string& name, const string& socket_path) {
void IPStackSimulator::listen(const string& name, const string& socket_path, FrameInfo::LinkType link_type) {
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);
this->add_socket(name, fd, link_type);
}
void IPStackSimulator::listen(const string& name, const string& addr, int port) {
void IPStackSimulator::listen(const string& name, const string& addr, int port, FrameInfo::LinkType link_type) {
if (port == 0) {
this->listen(name, addr);
this->listen(name, addr, link_type);
} 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);
this->add_socket(name, fd, link_type);
}
}
void IPStackSimulator::listen(const string& name, int port) {
this->listen(name, "", port);
void IPStackSimulator::listen(const string& name, int port, FrameInfo::LinkType link_type) {
this->listen(name, "", port, link_type);
}
void IPStackSimulator::add_socket(const string& name, int fd) {
void IPStackSimulator::add_socket(const string& name, int fd, FrameInfo::LinkType link_type) {
unique_listener l(
evconnlistener_new(
this->base.get(),
@@ -108,7 +166,7 @@ void IPStackSimulator::add_socket(const string& name, int fd) {
0,
fd),
evconnlistener_free);
this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, std::move(l)));
this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, link_type, std::move(l)));
}
uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) {
@@ -122,9 +180,10 @@ uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_ad
}
}
IPStackSimulator::IPClient::IPClient(shared_ptr<IPStackSimulator> sim, struct bufferevent* bev)
IPStackSimulator::IPClient::IPClient(shared_ptr<IPStackSimulator> sim, FrameInfo::LinkType link_type, struct bufferevent* bev)
: sim(sim),
bev(bev, bufferevent_free),
link_type(link_type),
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) {
@@ -196,7 +255,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<IPClient>(this->shared_from_this(), bev);
auto c = make_shared<IPClient>(this->shared_from_this(), listening_socket->link_type, bev);
this->bev_to_client.emplace(make_pair(bev, c));
bufferevent_setcb(bev, &IPStackSimulator::dispatch_on_client_input, nullptr,
@@ -274,21 +333,143 @@ void IPStackSimulator::on_client_error(struct bufferevent* bev, short events) {
}
}
void IPStackSimulator::on_client_frame(
shared_ptr<IPClient> c, const string& frame) {
if (ip_stack_simulator_log.debug("Virtual network sent frame")) {
print_data(stderr, frame);
fputc('\n', stderr);
}
this->log_frame(frame);
void IPStackSimulator::send_layer3_frame(shared_ptr<IPClient> c, FrameInfo::Protocol proto, const string& data) const {
this->send_layer3_frame(c, proto, data.data(), data.size());
}
FrameInfo fi(frame);
void IPStackSimulator::send_layer3_frame(shared_ptr<IPClient> 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: {
EthernetHeader ether;
ether.dest_mac = c->mac_addr;
ether.src_mac = this->host_mac_address_bytes;
switch (proto) {
case FrameInfo::Protocol::NONE:
throw logic_error("layer 3 protocol not specified");
case FrameInfo::Protocol::LCP:
throw logic_error("cannot send LCP frame over Ethernet");
case FrameInfo::Protocol::IPV4:
ether.protocol = 0x0800;
break;
case FrameInfo::Protocol::ARP:
ether.protocol = 0x0806;
break;
default:
throw logic_error("unknown layer 3 protocol");
}
le_uint16_t frame_size = size + sizeof(EthernetHeader);
evbuffer_add(out_buf, &frame_size, 2);
evbuffer_add(out_buf, &ether, sizeof(ether));
evbuffer_add(out_buf, data, size);
if (this->pcap_text_log_file) {
StringWriter w;
w.write(&ether, sizeof(ether));
w.write(data, size);
this->log_frame(w.str());
}
break;
}
case FrameInfo::LinkType::HDLC: {
HDLCHeader hdlc;
hdlc.start_sentinel1 = 0x7E;
hdlc.address = 0xFF;
hdlc.control = 0x03;
switch (proto) {
case FrameInfo::Protocol::NONE:
throw logic_error("layer 3 protocol not specified");
case FrameInfo::Protocol::LCP:
hdlc.protocol = 0xC021;
break;
case FrameInfo::Protocol::PAP:
hdlc.protocol = 0xC023;
break;
case FrameInfo::Protocol::IPCP:
hdlc.protocol = 0x8021;
break;
case FrameInfo::Protocol::IPV4:
hdlc.protocol = 0x0021;
break;
case FrameInfo::Protocol::ARP:
throw runtime_error("cannot send ARP packets over HDLC");
default:
throw logic_error("unknown layer 3 protocol");
}
StringWriter w;
w.put(hdlc);
w.write(data, size);
w.put_u16l(FrameInfo::computed_hdlc_checksum(w.str().data() + 1, w.size() - 1));
w.put_u8(0x7E);
string escaped = escape_hdlc_frame(w.str(), c->hdlc_escape_control_character_flags);
if (ip_stack_simulator_log.debug("Sending HDLC frame to virtual network (escaped to %zX bytes)", escaped.size())) {
print_data(stderr, w.str());
}
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);
}
break;
}
default:
throw logic_error("unknown link type");
}
}
void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& frame) {
const string* effective_data = &frame;
string hdlc_unescaped_data;
if (c->link_type == FrameInfo::LinkType::HDLC) {
hdlc_unescaped_data = unescape_hdlc_frame(frame);
effective_data = &hdlc_unescaped_data;
}
if (ip_stack_simulator_log.debug("Virtual network sent frame")) {
print_data(stderr, *effective_data);
}
this->log_frame(*effective_data);
FrameInfo fi(c->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());
}
if (fi.arp) {
if (fi.ether) {
if (c->mac_addr.is_filled_with(0)) {
c->mac_addr = fi.ether->src_mac;
} else if ((fi.ether->src_mac != c->mac_addr) && (fi.ether->src_mac != this->broadcast_mac_address_bytes)) {
throw runtime_error("client sent IPv4 packet from different MAC address");
}
} else if (fi.hdlc) {
uint16_t expected_checksum = fi.computed_hdlc_checksum();
uint16_t stored_checksum = fi.stored_hdlc_checksum();
if (expected_checksum != stored_checksum) {
throw runtime_error(string_printf(
"HDLC checksum is incorrect (%04hX expected, %04hX received)",
expected_checksum, stored_checksum));
}
} else {
throw runtime_error("frame is not Ethernet or HDLC");
}
if (fi.lcp) {
this->on_client_lcp_frame(c, fi);
} else if (fi.pap) {
this->on_client_pap_frame(c, fi);
} else if (fi.ipcp) {
this->on_client_ipcp_frame(c, fi);
} else if (fi.arp) {
this->on_client_arp_frame(c, fi);
} else if (fi.ipv4) {
@@ -299,12 +480,6 @@ void IPStackSimulator::on_client_frame(
expected_ipv4_checksum, fi.ipv4->checksum.load()));
}
// Populate the client's addresses if needed
if (c->mac_addr.is_filled_with(0)) {
c->mac_addr = fi.ether->src_mac;
} else if ((fi.ether->src_mac != c->mac_addr) && (fi.ether->src_mac != this->broadcast_mac_address_bytes)) {
throw runtime_error("client sent IPv4 packet from different MAC address");
}
if ((fi.ipv4->src_addr != c->ipv4_addr) && (fi.ipv4->src_addr != 0)) {
throw runtime_error("client sent IPv4 packet from different IPv4 address");
}
@@ -336,6 +511,261 @@ void IPStackSimulator::on_client_frame(
}
}
void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameInfo& fi) {
switch (fi.lcp->command) {
case 0x01: { // Configure-Request
auto opts_r = fi.read_payload();
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // Maximum receive unit
// TODO: Currently we ignore this, but we probably should use it.
opt_data_r.get_u16b();
break;
case 0x02: // Escaped control character flags
c->hdlc_escape_control_character_flags = opt_data_r.get_u32b();
break;
case 0x05: // Magic-Number
c->hdlc_remote_magic_number = opt_data_r.get_u32b();
break;
case 0x00: // RESERVED
case 0x03: // Authentication protocol
case 0x04: // Quality protocol
case 0x07: // Protocol field compression
case 0x08: // Address and control field compression
throw runtime_error(string_printf("unimplemented LCP option %02hhX (%zu bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown LCP option");
}
}
// Technically, we should implement the LCP state machine, but I'm too
// lazy to do this right now. In our situation, it should suffice to
// simply always send a Configure-Request to the client with a magic
// number not equal to the one we received.
StringWriter opts_w;
opts_w.put_u8(0x01); // Maximum receive unit
opts_w.put_u8(0x04);
opts_w.put_u16b(1500);
opts_w.put_u8(0x02); // Escaped control character flags (we don't require any)
opts_w.put_u8(0x06);
opts_w.put_u32(0);
opts_w.put_u8(0x03); // Authentication protocol
opts_w.put_u8(0x04);
opts_w.put_u16b(0xC023); // Password authentication protocol
opts_w.put_u8(0x05); // Magic number (bitwise inverse of the remote end's)
opts_w.put_u8(0x06);
opts_w.put_u32b(~c->hdlc_remote_magic_number);
StringWriter request_w;
request_w.put<LCPHeader>(LCPHeader{
.command = 0x01, // Configure-Request
.request_id = fi.lcp->request_id,
.size = static_cast<uint16_t>(sizeof(LCPHeader) + opts_w.size()),
});
request_w.write(opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, request_w.str());
StringWriter ack_w;
ack_w.put<LCPHeader>(LCPHeader{
.command = 0x02, // Configure-Ack
.request_id = fi.lcp->request_id,
.size = fi.lcp->size,
});
ack_w.write(fi.payload, fi.payload_size);
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, ack_w.str());
break;
}
case 0x05: { // Terminate-Request
c->ipv4_addr = 0;
c->tcp_connections.clear();
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x06; // Terminate-Ack
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
}
case 0x09: { // Echo-Request
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x0A; // Echo-Reply
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
}
case 0x0B: // Discard-Request
case 0x02: // Configure-Ack
break;
case 0x03: // Configure-Nak
case 0x04: // Configure-Reject
case 0x06: // Terminate-Ack
case 0x07: // Code-Reject
case 0x08: // Protocol-Reject
case 0x0A: // Echo-Reply
throw runtime_error("unimplemented LCP command");
default:
throw runtime_error("unknown LCP command");
}
}
void IPStackSimulator::on_client_pap_frame(shared_ptr<IPClient> c, const FrameInfo& fi) {
if (fi.pap->command != 0x01) { // Authenticate-Request
throw runtime_error("client sent incorrect PAP command");
}
auto r = fi.read_payload();
string username = r.read(r.get_u8());
string password = r.read(r.get_u8());
ip_stack_simulator_log.info("Client logged in with username \"%s\" and password", username.c_str());
static const string login_message = "newserv PPP simulator";
StringWriter w;
w.put<PAPHeader>(PAPHeader{
.command = 0x02, // Authenticate-Ack
.request_id = fi.pap->request_id,
.size = login_message.size() + sizeof(PAPHeader) + 1,
});
w.put_u8(login_message.size());
w.write(login_message);
this->send_layer3_frame(c, FrameInfo::Protocol::PAP, w.str());
}
void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameInfo& fi) {
switch (fi.ipcp->command) {
case 0x01: { // Configure-Request
auto opts_r = fi.read_payload();
uint32_t remote_ip = 0;
uint32_t remote_primary_dns = 0;
uint32_t remote_secondary_dns = 0;
StringWriter rejected_opts_w;
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // IP addresses (deprecated as of 1992; we don't support it at all)
throw runtime_error("IPCP client sent IP-Addresses option");
case 0x02: // IP compression protocol
rejected_opts_w.put_u8(0x02);
rejected_opts_w.put_u8(opt_data_r.size() + 2);
rejected_opts_w.write(opt_data);
break;
case 0x03: // IP address
remote_ip = opt_data_r.get_u32b();
break;
case 0x81: // Primary DNS server address
remote_primary_dns = opt_data_r.get_u32b();
break;
case 0x83: // Secondary DNS server address
remote_secondary_dns = opt_data_r.get_u32b();
break;
case 0x82: // Primary NBNS server address
case 0x84: // Secondary NBNS server address
case 0x04: // Mobile IP address
throw runtime_error(string_printf("unimplemented IPCP option %02hhX (%zu bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown IPCP option");
}
}
if (!rejected_opts_w.str().empty()) {
// Send a Configure-Reject if the client specified IP header compression
StringWriter reject_w;
reject_w.put<IPCPHeader>(IPCPHeader{
.command = 0x04, // Configure-Reject
.request_id = fi.ipcp->request_id,
.size = sizeof(IPCPHeader) + rejected_opts_w.size(),
});
reject_w.write(rejected_opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::IPCP, reject_w.str());
} else if ((remote_ip != 0x1E1E1E1E) ||
(remote_primary_dns != 0x23232323) ||
(remote_secondary_dns != 0x24242424)) {
// Send a Configure-Nak if the client's request doesn't exactly match
// what we want them to use.
StringWriter opts_w;
opts_w.put_u8(0x03); // IP address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x1E1E1E1E);
opts_w.put_u8(0x81); // Primary DNS server address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x23232323);
opts_w.put_u8(0x83); // Secondary DNS server address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x24242424);
StringWriter nak_w;
nak_w.put<IPCPHeader>(IPCPHeader{
.command = 0x03, // Configure-Nak
.request_id = fi.ipcp->request_id,
.size = static_cast<uint16_t>(opts_w.size() + sizeof(IPCPHeader)),
});
nak_w.write(opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::IPCP, nak_w.str());
} else { // Options OK
c->ipv4_addr = remote_ip;
// As with LCP, we technically should implement the state machine, but I
// continue to be lazy.
StringWriter opts_w;
opts_w.put_u8(0x03); // IP address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x39393939);
opts_w.put_u8(0x81); // Primary DNS server address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x23232323);
opts_w.put_u8(0x83); // Secondary DNS server address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x24242424);
StringWriter request_w;
request_w.put<IPCPHeader>(IPCPHeader{
.command = 0x01, // Configure-Request
.request_id = fi.ipcp->request_id,
.size = static_cast<uint16_t>(opts_w.size() + sizeof(IPCPHeader)),
});
request_w.write(opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::IPCP, request_w.str());
StringWriter ack_w;
ack_w.put<IPCPHeader>(IPCPHeader{
.command = 0x02, // Configure-Ack
.request_id = fi.ipcp->request_id,
.size = fi.ipcp->size,
});
ack_w.write(fi.payload, fi.payload_size);
this->send_layer3_frame(c, FrameInfo::Protocol::IPCP, ack_w.str());
}
break;
}
case 0x05: { // Terminate-Request
c->ipv4_addr = 0;
c->tcp_connections.clear();
string response(reinterpret_cast<const char*>(fi.payload), fi.payload_size);
response.at(0) = 0x06; // Terminate-Ack
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, response);
break;
}
case 0x02: // Configure-Ack
break;
case 0x03: // Configure-Nak
case 0x04: // Configure-Reject
case 0x06: // Terminate-Ack
case 0x07: // Code-Reject
throw runtime_error("unimplemented IPCP command");
default:
throw runtime_error("unknown LCP command");
}
}
void IPStackSimulator::on_client_arp_frame(
shared_ptr<IPClient> c, const FrameInfo& fi) {
if (fi.arp->hwaddr_len != 6 ||
@@ -353,17 +783,14 @@ void IPStackSimulator::on_client_arp_frame(
reinterpret_cast<const uint8_t*>(fi.payload) + 6);
}
EthernetHeader r_ether;
r_ether.dest_mac = fi.ether->src_mac;
r_ether.src_mac = this->host_mac_address_bytes;
r_ether.protocol = fi.ether->protocol;
ARPHeader r_arp;
r_arp.hardware_type = fi.arp->hardware_type;
r_arp.protocol_type = fi.arp->protocol_type;
r_arp.hwaddr_len = 6;
r_arp.paddr_len = 4;
r_arp.operation = 0x0002;
StringWriter w;
w.put<ARPHeader>(ARPHeader{
.hardware_type = fi.arp->hardware_type,
.protocol_type = fi.arp->protocol_type,
.hwaddr_len = 6,
.paddr_len = 4,
.operation = 0x0002,
});
// The incoming payload is:
// uint8_t src_mac[6]; // MAC address of client
@@ -375,43 +802,19 @@ void IPStackSimulator::on_client_arp_frame(
// uint8_t dest_ip[4]; // IP address of host
// uint8_t src_mac[6]; // MAC address of client
// uint8_t src_ip[4]; // IP address of client
const char* payload_bytes = reinterpret_cast<const char*>(fi.payload);
w.write(this->host_mac_address_bytes.data(), 6);
w.write(payload_bytes + 16, 4);
w.write(payload_bytes, 10);
uint8_t r_payload[20];
memcpy(&r_payload[0], this->host_mac_address_bytes.data(), 6);
memcpy(&r_payload[6], payload_bytes + 16, 4);
memcpy(&r_payload[10], payload_bytes, 10);
struct evbuffer* out_buf = bufferevent_get_output(c->bev.get());
uint16_t frame_size = sizeof(r_ether) + sizeof(r_arp) + sizeof(r_payload);
evbuffer_add(out_buf, &frame_size, 2);
evbuffer_add(out_buf, &r_ether, sizeof(r_ether));
evbuffer_add(out_buf, &r_arp, sizeof(r_arp));
evbuffer_add(out_buf, r_payload, sizeof(r_payload));
ip_stack_simulator_log.debug("Sending ARP response");
if (this->pcap_text_log_file) {
StringWriter w;
w.write(&r_ether, sizeof(r_ether));
w.write(&r_arp, sizeof(r_arp));
w.write(r_payload, sizeof(r_payload));
this->log_frame(w.str());
}
this->send_layer3_frame(c, FrameInfo::Protocol::ARP, w.str());
}
void IPStackSimulator::on_client_udp_frame(
shared_ptr<IPClient> c, const FrameInfo& fi) {
// We only implement DHCP and newserv's DNS server here
void IPStackSimulator::on_client_udp_frame(shared_ptr<IPClient> c, const FrameInfo& fi) {
// We only implement DHCP and newserv's DNS server here.
// Every received UDP packet will elicit exactly one UDP response from
// newserv, so we prepare the response headers in advance
EthernetHeader r_ether;
r_ether.dest_mac = fi.ether->src_mac;
r_ether.src_mac = this->host_mac_address_bytes;
r_ether.protocol = fi.ether->protocol;
IPv4Header r_ipv4;
r_ipv4.version_ihl = 0x45;
@@ -433,7 +836,7 @@ void IPStackSimulator::on_client_udp_frame(
string r_data;
if (fi.udp->dest_port == 67) { // DHCP
StringReader r(fi.payload, fi.payload_size);
auto r = fi.read_payload();
const auto& dhcp = r.get<DHCPHeader>();
if (dhcp.hardware_type != 1) {
throw runtime_error("unknown DHCP hardware type");
@@ -566,29 +969,18 @@ void IPStackSimulator::on_client_udp_frame(
r_udp.checksum = FrameInfo::computed_udp4_checksum(
r_ipv4, r_udp, r_data.data(), r_data.size());
struct evbuffer* out_buf = bufferevent_get_output(c->bev.get());
if (ip_stack_simulator_log.should_log(LogLevel::DEBUG)) {
string remote_str = this->str_for_ipv4_netloc(fi.ipv4->src_addr, fi.udp->src_port);
ip_stack_simulator_log.debug("Sending UDP response to %s", remote_str.c_str());
print_data(stderr, r_data);
}
uint16_t frame_size = sizeof(r_ether) + sizeof(r_ipv4) + sizeof(r_udp) + r_data.size();
evbuffer_add(out_buf, &frame_size, 2);
evbuffer_add(out_buf, &r_ether, sizeof(r_ether));
evbuffer_add(out_buf, &r_ipv4, sizeof(r_ipv4));
evbuffer_add(out_buf, &r_udp, sizeof(r_udp));
evbuffer_add(out_buf, r_data.data(), r_data.size());
StringWriter w;
w.put(r_ipv4);
w.put(r_udp);
w.write(r_data);
if (this->pcap_text_log_file) {
StringWriter w;
w.write(&r_ether, sizeof(r_ether));
w.write(&r_ipv4, sizeof(r_ipv4));
w.write(&r_udp, sizeof(r_udp));
w.write(r_data.data(), r_data.size());
this->log_frame(w.str());
}
this->send_layer3_frame(c, FrameInfo::Protocol::IPV4, w.str());
}
}
@@ -863,8 +1255,7 @@ void IPStackSimulator::on_client_tcp_frame(
}
}
void IPStackSimulator::open_server_connection(
shared_ptr<IPClient> c, IPClient::TCPConnection& conn) {
void IPStackSimulator::open_server_connection(shared_ptr<IPClient> c, IPClient::TCPConnection& conn) {
if (conn.server_bev.get()) {
throw logic_error("server connection is already open");
}
@@ -913,20 +1304,25 @@ void IPStackSimulator::open_server_connection(
}
}
void IPStackSimulator::send_pending_push_frame(
shared_ptr<IPClient> c, IPClient::TCPConnection& conn) {
void IPStackSimulator::send_pending_push_frame(shared_ptr<IPClient> c, IPClient::TCPConnection& conn) {
size_t pending_bytes = evbuffer_get_length(conn.pending_data.get());
if (!pending_bytes) {
return;
}
size_t bytes_to_send = min<size_t>(pending_bytes, conn.next_push_max_frame_size);
if ((c->link_type == FrameInfo::LinkType::HDLC) && (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
// frame.
bytes_to_send = 200;
}
ip_stack_simulator_log.debug("Sending PSH frame with seq_num %08" PRIX32 ", 0x%zX/0x%zX data bytes",
conn.acked_server_seq, bytes_to_send, pending_bytes);
this->send_tcp_frame(c, conn, TCPHeader::Flag::PSH, conn.pending_data.get(),
bytes_to_send);
this->send_tcp_frame(c, conn, TCPHeader::Flag::PSH, conn.pending_data.get(), bytes_to_send);
struct timeval resend_push_timeout = usecs_to_timeval(conn.resend_push_usecs);
event_add(conn.resend_push_event.get(), &resend_push_timeout);
@@ -953,11 +1349,6 @@ void IPStackSimulator::send_tcp_frame(
throw logic_error("data should be given if and only if PSH is given");
}
EthernetHeader ether;
ether.dest_mac = c->mac_addr;
ether.src_mac = this->host_mac_address_bytes;
ether.protocol = 0x0800; // IPv4
IPv4Header ipv4;
ipv4.version_ihl = 0x45;
ipv4.tos = 0;
@@ -984,28 +1375,16 @@ void IPStackSimulator::send_tcp_frame(
ipv4.checksum = FrameInfo::computed_ipv4_header_checksum(ipv4);
const void* linear_data = src_bytes ? evbuffer_pullup(src_buf, src_bytes) : nullptr;
tcp.checksum = FrameInfo::computed_tcp4_checksum(
ipv4, tcp, linear_data, src_bytes);
tcp.checksum = FrameInfo::computed_tcp4_checksum(ipv4, tcp, linear_data, src_bytes);
struct evbuffer* out_buf = bufferevent_get_output(c->bev.get());
uint16_t frame_size = sizeof(ether) + sizeof(ipv4) + sizeof(tcp) + src_bytes;
evbuffer_add(out_buf, &frame_size, 2);
evbuffer_add(out_buf, &ether, sizeof(ether));
evbuffer_add(out_buf, &ipv4, sizeof(ipv4));
evbuffer_add(out_buf, &tcp, sizeof(tcp));
StringWriter w;
w.put(ipv4);
w.put(tcp);
if (src_bytes) {
evbuffer_add(out_buf, linear_data, src_bytes);
w.write(linear_data, src_bytes);
}
if (this->pcap_text_log_file) {
StringWriter w;
w.write(&ether, sizeof(ether));
w.write(&ipv4, sizeof(ipv4));
w.write(&tcp, sizeof(tcp));
w.write(linear_data, src_bytes);
this->log_frame(w.str());
}
this->send_layer3_frame(c, FrameInfo::Protocol::IPV4, w.str());
}
void IPStackSimulator::dispatch_on_resend_push(evutil_socket_t, short, void* ctx) {
+18 -7
View File
@@ -21,10 +21,10 @@ public:
std::shared_ptr<ServerState> state);
~IPStackSimulator();
void listen(const std::string& name, const std::string& socket_path);
void listen(const std::string& name, const std::string& addr, int port);
void listen(const std::string& name, int port);
void add_socket(const std::string& name, int fd);
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);
static uint32_t connect_address_for_remote_address(uint32_t remote_addr);
@@ -41,7 +41,10 @@ private:
std::weak_ptr<IPStackSimulator> sim;
unique_bufferevent bev;
parray<uint8_t, 6> mac_addr;
FrameInfo::LinkType link_type;
uint32_t hdlc_escape_control_character_flags = 0xFFFFFFFF;
uint32_t hdlc_remote_magic_number = 0;
parray<uint8_t, 6> mac_addr; // Only used for LinkType::ETHERNET
uint32_t ipv4_addr;
struct TCPConnection {
@@ -77,7 +80,7 @@ private:
unique_event idle_timeout_event;
IPClient(std::shared_ptr<IPStackSimulator> sim, struct bufferevent* bev);
IPClient(std::shared_ptr<IPStackSimulator> sim, FrameInfo::LinkType link_type, struct bufferevent* bev);
static void dispatch_on_idle_timeout(evutil_socket_t fd, short events, void* ctx);
void on_idle_timeout();
@@ -85,10 +88,12 @@ private:
struct ListeningSocket {
std::string name;
FrameInfo::LinkType link_type;
unique_listener listener;
ListeningSocket(const std::string& name, unique_listener&& l)
ListeningSocket(const std::string& name, FrameInfo::LinkType link_type, unique_listener&& l)
: name(name),
link_type(link_type),
listener(std::move(l)) {}
};
@@ -122,7 +127,13 @@ private:
static void dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx);
void on_client_error(struct bufferevent* bev, short events);
void send_layer3_frame(std::shared_ptr<IPClient> c, FrameInfo::Protocol proto, const std::string& data) const;
void send_layer3_frame(std::shared_ptr<IPClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const;
void on_client_frame(std::shared_ptr<IPClient> c, const std::string& frame);
void on_client_lcp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
void on_client_pap_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
void on_client_ipcp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
void on_client_arp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
void on_client_udp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
void on_client_tcp_frame(std::shared_ptr<IPClient> c, const FrameInfo& fi);
+8 -3
View File
@@ -1767,13 +1767,18 @@ Action a_run_server_replay_log(
}
}
if (!state->ip_stack_addresses.empty()) {
config_log.info("Starting IP stack simulator");
if (!state->ip_stack_addresses.empty() || !state->ppp_stack_addresses.empty()) {
config_log.info("Starting IP/PPP stack simulator");
ip_stack_simulator = make_shared<IPStackSimulator>(base, state);
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);
ip_stack_simulator->listen(spec, netloc.first, netloc.second, FrameInfo::LinkType::ETHERNET);
}
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);
}
}
}
+10
View File
@@ -581,6 +581,16 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
}
} catch (const out_of_range&) {
}
try {
for (const auto& item : json.at("PPPStackListen").as_list()) {
if (item->is_int()) {
this->ppp_stack_addresses.emplace_back(string_printf("0.0.0.0:%" PRId64, item->as_int()));
} else {
this->ppp_stack_addresses.emplace_back(item->as_string());
}
}
} catch (const out_of_range&) {
}
}
auto local_address_str = json.at("LocalAddress").as_string();
+1
View File
@@ -65,6 +65,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::string username;
uint16_t dns_server_port;
std::vector<std::string> ip_stack_addresses;
std::vector<std::string> ppp_stack_addresses;
bool ip_stack_debug;
bool allow_unregistered_users;
bool allow_dc_pc_games;
+8 -7
View File
@@ -128,19 +128,20 @@
"bb-data2": [12005, "bb", "login_server"],
},
// Where to listen for IP stack clients. This exists to interface with PSO GC
// clients running in a local Dolphin emulator. To enable local Dolphin
// clients to connect, set this to ["/tmp/dolphin-tap"] and configure Dolphin
// to use the tapserver type of broadband adapter. You do not need to install
// or run tapserver. See README.md for details on how to get PSO to connect
// via this interface. You can also add numbers or "address:port" strings to
// this list to listen for tapserver connections on a TCP port.
// Where to listen for IP and PPP stack clients. This exists to interface with
// PSO GC clients running in a local Dolphin emulator. To enable local Dolphin
// clients to connect, set IPStackListen to ["/tmp/dolphin-tap"] and configure
// Dolphin to use the tapserver type of broadband adapter. You do not need to
// install or run tapserver. See README.md for details on how to get PSO to
// connect via this interface. You can also add numbers or "address:port"
// strings to these lists to listen for tapserver connections on TCP ports.
// On Windows, Unix sockets are not available, so you can only use TCP sockets
// here. You can get Dolphin to connect locally by adding a port to this list
// and configuring Dolphin to connect to the same port. For example, you could
// set this to ["127.0.0.1:5059"], and configure Dolphin's tapserver BBA to
// connect to 127.0.0.1:5059.
"IPStackListen": [],
"PPPStackListen": [],
// Other servers to support proxying to. If this is empty for any game
// version, the proxy server is disabled for that version. Entries in these
+1
View File
@@ -18,6 +18,7 @@
"DNSServerPort": 0,
"IPStackListen": [],
"PPPStackListen": [],
"EnableItemTracking": true,
"Episode3BehaviorFlags": 0xFA,