From 355f3b2bd0cf56ecf5ae540a4ea3324d67577561 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 18 Sep 2019 17:35:14 +0900 Subject: [PATCH] Add support for link-local ipv6 addr --- cpp_impl/src/UDPC_Defines.hpp | 3 +- cpp_impl/src/UDPConnection.cpp | 296 ++++++++++++++++++++++--- cpp_impl/src/UDPConnection.h | 9 +- cpp_impl/src/test/TestUDPC.cpp | 15 +- cpp_impl/src/test/UDPC_NetworkTest.cpp | 15 +- 5 files changed, 294 insertions(+), 44 deletions(-) diff --git a/cpp_impl/src/UDPC_Defines.hpp b/cpp_impl/src/UDPC_Defines.hpp index 162b56b..0de0a17 100644 --- a/cpp_impl/src/UDPC_Defines.hpp +++ b/cpp_impl/src/UDPC_Defines.hpp @@ -69,7 +69,7 @@ struct IPV6_Hasher { struct ConnectionData { ConnectionData(); - ConnectionData(bool isServer, Context *ctx, struct in6_addr addr, uint16_t port); + ConnectionData(bool isServer, Context *ctx, struct in6_addr addr, uint32_t scope_id, uint16_t port); // copy ConnectionData(const ConnectionData& other) = delete; @@ -98,6 +98,7 @@ struct ConnectionData { std::chrono::steady_clock::duration toggleTimer; std::chrono::steady_clock::duration toggledTimer; struct in6_addr addr; // in network order + uint32_t scope_id; uint16_t port; // in native order std::deque sentPkts; TSQueue sendPkts; diff --git a/cpp_impl/src/UDPConnection.cpp b/cpp_impl/src/UDPConnection.cpp index 5000450..ac76ae2 100644 --- a/cpp_impl/src/UDPConnection.cpp +++ b/cpp_impl/src/UDPConnection.cpp @@ -12,11 +12,13 @@ #include #include #include +#include +#include -static std::regex ipv6_regex = std::regex(R"d((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))d"); -// TODO remove ipv6_regex_nolink when link device is supported -static std::regex ipv6_regex_nolink = std::regex(R"d((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))d"); -static std::regex ipv4_regex = std::regex(R"d((1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9]))d"); +//static const std::regex ipv6_regex = std::regex(R"d((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))d"); +static const std::regex ipv6_regex_nolink = std::regex(R"d((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))d"); +static const std::regex ipv6_regex_linkonly = std::regex(R"d(fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,})d"); +static const std::regex ipv4_regex = std::regex(R"d((1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9])\.(1[0-9][0-9]|2[0-4][0-9]|25[0-5]|[1-9][0-9]|[0-9]))d"); UDPC::SentPktInfo::SentPktInfo() : id(0), @@ -25,6 +27,10 @@ sentTime(std::chrono::steady_clock::now()) std::size_t UDPC::ConnectionIdHasher::operator()(const UDPC_ConnectionId& key) const { std::string value((const char*)key.addr.s6_addr, 16); + value.push_back((char)((key.scope_id >> 24) & 0xFF)); + value.push_back((char)((key.scope_id >> 16) & 0xFF)); + value.push_back((char)((key.scope_id >> 8) & 0xFF)); + value.push_back((char)(key.scope_id & 0xFF)); value.push_back((char)((key.port >> 8) & 0xFF)); value.push_back((char)(key.port & 0xFF)); return std::hash()(value); @@ -35,7 +41,7 @@ std::size_t UDPC::IPV6_Hasher::operator()(const struct in6_addr& addr) const { } bool operator ==(const UDPC_ConnectionId& a, const UDPC_ConnectionId& b) { - return a.addr == b.addr && a.port == b.port; + return a.addr == b.addr && a.scope_id == b.scope_id && a.port == b.port; } bool operator ==(const struct in6_addr& a, const struct in6_addr& b) { @@ -71,7 +77,12 @@ rtt(std::chrono::steady_clock::duration::zero()) flags.reset(1); } -UDPC::ConnectionData::ConnectionData(bool isServer, Context *ctx, struct in6_addr addr, uint16_t port) : +UDPC::ConnectionData::ConnectionData( + bool isServer, + Context *ctx, + struct in6_addr addr, + uint32_t scope_id, + uint16_t port) : flags(), id(0), lseq(0), @@ -82,6 +93,7 @@ toggleT(UDPC::THIRTY_SECONDS), toggleTimer(std::chrono::steady_clock::duration::zero()), toggledTimer(std::chrono::steady_clock::duration::zero()), addr(addr), +scope_id(scope_id), port(port), sentPkts(), sendPkts(UDPC_QUEUED_PKTS_MAX_SIZE), @@ -154,7 +166,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::VERBOSE, "Timed out connection with ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); continue; @@ -168,7 +180,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::INFO, "Switching to bad mode in connection with ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); iter->second.flags.reset(1); @@ -194,7 +206,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::INFO, "Switching to good mode in connection with ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); iter->second.flags.set(1); @@ -272,7 +284,7 @@ void UDPC::Context::update_impl() { std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); destinationInfo.sin6_port = htons(iter->second.port); destinationInfo.sin6_flowinfo = 0; - destinationInfo.sin6_scope_id = 0; + destinationInfo.sin6_scope_id = iter->first.scope_id; long int sentBytes = sendto( socketHandle, buf.get(), @@ -284,13 +296,13 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::ERROR, "Failed to send packet to initiate connection to ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); continue; } else { log(UDPC_LoggingType::INFO, "Sent initiate connection to ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); } @@ -314,7 +326,7 @@ void UDPC::Context::update_impl() { std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); destinationInfo.sin6_port = htons(iter->second.port); destinationInfo.sin6_flowinfo = 0; - destinationInfo.sin6_scope_id = 0; + destinationInfo.sin6_scope_id = iter->first.scope_id; long int sentBytes = sendto( socketHandle, buf.get(), @@ -326,7 +338,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::ERROR, "Failed to send packet to initiate connection to ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); continue; @@ -358,7 +370,7 @@ void UDPC::Context::update_impl() { std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16); destinationInfo.sin6_port = htons(iter->second.port); destinationInfo.sin6_flowinfo = 0; - destinationInfo.sin6_scope_id = 0; + destinationInfo.sin6_scope_id = iter->first.scope_id; long int sentBytes = sendto( socketHandle, buf.get(), @@ -370,7 +382,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::ERROR, "Failed to send heartbeat packet to ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); continue; @@ -429,7 +441,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::ERROR, "Failed to send packet to ", - UDPC_atostr((UDPC_HContext)this, iter->first), + UDPC_atostr((UDPC_HContext)this, iter->first.addr), ", port = ", iter->second.port); continue; @@ -492,7 +504,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::INFO, "Received packet is smaller than header, ignoring packet from ", - UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}), + UDPC_atostr((UDPC_HContext)this, receivedData.sin6_addr), ", port = ", receivedData.sin6_port); return; @@ -504,7 +516,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::INFO, "Received packet has invalid protocol id, ignoring packet from ", - UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}), + UDPC_atostr((UDPC_HContext)this, receivedData.sin6_addr), ", port = ", ntohs(receivedData.sin6_port)); return; @@ -521,18 +533,18 @@ void UDPC::Context::update_impl() { bool isResending = conID & UDPC_ID_RESENDING; conID &= 0x0FFFFFFF; - UDPC_ConnectionId identifier{receivedData.sin6_addr, ntohs(receivedData.sin6_port)}; + UDPC_ConnectionId identifier{receivedData.sin6_addr, receivedData.sin6_scope_id, ntohs(receivedData.sin6_port)}; if(isConnect && flags.test(2)) { // is connect packet and is accepting new connections if(!flags.test(1) && conMap.find(identifier) == conMap.end()) { // is receiving as server, connection did not already exist - UDPC::ConnectionData newConnection(true, this, receivedData.sin6_addr, ntohs(receivedData.sin6_port)); + UDPC::ConnectionData newConnection(true, this, receivedData.sin6_addr, receivedData.sin6_scope_id, ntohs(receivedData.sin6_port)); log( UDPC_LoggingType::VERBOSE, "Establishing connection with client ", - UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}), + UDPC_atostr((UDPC_HContext)this, receivedData.sin6_addr), ", port = ", ntohs(receivedData.sin6_port), ", giving client id = ", newConnection.id); @@ -564,7 +576,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::VERBOSE, "Established connection with server ", - UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}), + UDPC_atostr((UDPC_HContext)this, receivedData.sin6_addr), ", port = ", ntohs(receivedData.sin6_port), ", got id = ", conID); @@ -585,7 +597,7 @@ void UDPC::Context::update_impl() { log( UDPC_LoggingType::INFO, "Received valid packet from ", - UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}), + UDPC_atostr((UDPC_HContext)this, receivedData.sin6_addr), ", port = ", ntohs(receivedData.sin6_port), ", packet id = ", seqID, @@ -829,10 +841,12 @@ UDPC_PacketInfo UDPC::get_empty_pinfo() { 0, // dataSize { // sender {0}, // ipv6 addr + 0, // scope_id 0 // port }, { // receiver {0}, // ipv6 addr + 0, // scope_id 0 // port }, }; @@ -852,11 +866,15 @@ void UDPC::threadedUpdate(Context *ctx) { } UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port) { - return UDPC_ConnectionId{addr, port}; + return UDPC_ConnectionId{addr, 0, port}; +} + +UDPC_ConnectionId UDPC_create_id_full(struct in6_addr addr, uint32_t scope_id, uint16_t port) { + return UDPC_ConnectionId{addr, scope_id, port}; } UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port) { - return UDPC_ConnectionId{in6addr_any, port}; + return UDPC_ConnectionId{in6addr_any, 0, port}; } UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) { @@ -864,7 +882,7 @@ UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) { ctx->flags.set(1, isClient != 0); ctx->log(UDPC_LoggingType::INFO, "Got listen addr ", - UDPC_atostr((UDPC_HContext)ctx, listenId)); + UDPC_atostr((UDPC_HContext)ctx, listenId.addr)); // create socket ctx->socketHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); @@ -886,7 +904,7 @@ UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) { ctx->socketInfo.sin6_addr = listenId.addr; ctx->socketInfo.sin6_port = htons(listenId.port); ctx->socketInfo.sin6_flowinfo = 0; - ctx->socketInfo.sin6_scope_id = 0; + ctx->socketInfo.sin6_scope_id = listenId.scope_id; if(bind(ctx->socketHandle, (const struct sockaddr *)&ctx->socketInfo, sizeof(struct sockaddr_in6)) < 0) { // TODO maybe different way of handling init fail @@ -969,12 +987,12 @@ void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connec } c->log(UDPC_LoggingType::INFO, "client_initiate_connection: Got peer a = ", - UDPC_atostr((UDPC_HContext)ctx, connectionId), + UDPC_atostr((UDPC_HContext)ctx, connectionId.addr), ", p = ", connectionId.port); std::lock_guard lock(c->mutex); - UDPC::ConnectionData newCon(false, c, connectionId.addr, connectionId.port); + UDPC::ConnectionData newCon(false, c, connectionId.addr, connectionId.scope_id, connectionId.port); newCon.sent = std::chrono::steady_clock::now() - UDPC::INIT_PKT_INTERVAL_DT; if(c->conMap.find(connectionId) == c->conMap.end()) { @@ -1139,7 +1157,11 @@ UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx) { return UDPC::get_empty_pinfo(); } -const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { +const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { + return UDPC_atostr(ctx, connectionId.addr); +} + +const char *UDPC_atostr(UDPC_HContext ctx, struct in6_addr addr) { UDPC::Context *c = UDPC::verifyContext(ctx); if(!c) { return nullptr; @@ -1168,11 +1190,11 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { } } - if(connectionId.addr.s6_addr[i] == 0) { + if(addr.s6_addr[i] == 0) { continue; } else { std::stringstream sstream; - sstream << std::hex << (unsigned int) connectionId.addr.s6_addr[i]; + sstream << std::hex << (unsigned int) addr.s6_addr[i]; std::string out(sstream.str()); if(out.size() == 1) { if(out[0] != '0') { @@ -1198,7 +1220,6 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId) { struct in6_addr UDPC_strtoa(const char *addrStr) { struct in6_addr result = in6addr_loopback; std::cmatch matchResults; - // TODO switch regex to ipv6_regex when link device is supported if(std::regex_match(addrStr, matchResults, ipv6_regex_nolink)) { unsigned int index = 0; unsigned int strIndex = 0; @@ -1376,3 +1397,210 @@ struct in6_addr UDPC_strtoa(const char *addrStr) { } return result; } + +struct in6_addr UDPC_strtoa_link(const char *addrStr, uint32_t *linkId_out) { + const auto checkSetOut = [&linkId_out] (uint32_t val) { + if(linkId_out) { + *linkId_out = val; + } + }; + + struct in6_addr result({0}); + std::cmatch matchResults; + const char *linkName = nullptr; + + if(std::regex_match(addrStr, matchResults, ipv6_regex_linkonly)) { + unsigned int index = 0; + unsigned int strIndex = 0; + int doubleColonIndex = -1; + unsigned char bytes[2] = {0, 0}; + unsigned char bytesState = 0; + bool prevColon = false; + + const auto checkInc = [&result, &index, &bytes] () -> bool { + if(index < 15) { + result.s6_addr[index++] = bytes[0]; + result.s6_addr[index++] = bytes[1]; + bytes[0] = 0; + bytes[1] = 0; + return false; + } + return true; + }; + + while(addrStr[strIndex] != '%') { + if(addrStr[strIndex] >= '0' && addrStr[strIndex] <= '9') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - '0'); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - '0'); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - '0'); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - '0'); + bytesState = 0; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + default: + checkSetOut(0); + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] >= 'a' && addrStr[strIndex] <= 'f') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - 'a' + 10); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - 'a' + 10); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - 'a' + 10); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - 'a' + 10); + bytesState = 0; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + default: + checkSetOut(0); + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] >= 'A' && addrStr[strIndex] <= 'F') { + switch(bytesState) { + case 0: + bytes[0] = (addrStr[strIndex] - 'A' + 10); + bytesState = 1; + break; + case 1: + bytes[0] = (bytes[0] << 4) | (addrStr[strIndex] - 'A' + 10); + bytesState = 2; + break; + case 2: + bytes[1] = (addrStr[strIndex] - 'A' + 10); + bytesState = 3; + break; + case 3: + bytes[1] = (bytes[1] << 4) | (addrStr[strIndex] - 'A' + 10); + bytesState = 0; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + default: + checkSetOut(0); + return in6addr_loopback; + } + prevColon = false; + } else if(addrStr[strIndex] == ':') { + switch(bytesState) { + case 1: + case 2: + bytes[1] = bytes[0]; + bytes[0] = 0; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + case 3: + bytes[1] |= (bytes[0] & 0xF) << 4; + bytes[0] = bytes[0] >> 4; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + case 0: + break; + default: + checkSetOut(0); + return in6addr_loopback; + } + bytesState = 0; + if(prevColon) { + if(doubleColonIndex >= 0) { + checkSetOut(0); + return in6addr_loopback; + } else { + doubleColonIndex = index; + } + } else { + prevColon = true; + } + } else { + checkSetOut(0); + return in6addr_loopback; + } + + ++strIndex; + } + switch(bytesState) { + case 1: + case 2: + bytes[1] = bytes[0]; + bytes[0] = 0; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + case 3: + bytes[1] |= (bytes[0] & 0xF) << 4; + bytes[0] = bytes[0] >> 4; + if(checkInc()) { + checkSetOut(0); + return in6addr_loopback; + } + break; + case 0: + break; + default: + checkSetOut(0); + return in6addr_loopback; + } + linkName = addrStr + strIndex + 1; + + if(doubleColonIndex >= 0) { + strIndex = 16 - index; + if(strIndex < 2) { + checkSetOut(0); + return in6addr_loopback; + } + for(unsigned int i = 16; i-- > (unsigned int)doubleColonIndex + strIndex; ) { + result.s6_addr[i] = result.s6_addr[i - strIndex]; + result.s6_addr[i - strIndex] = 0; + } + } + } + struct ifreq req{{0}, 0, 0}; + std::strncpy(req.ifr_name, linkName, IFNAMSIZ); + int socketHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if(ioctl(socketHandle, SIOCGIFINDEX, &req) < 0) { + CleanupSocket(socketHandle); + checkSetOut(0); + return in6addr_loopback; + } + + CleanupSocket(socketHandle); + checkSetOut(req.ifr_ifindex); + return result; +} diff --git a/cpp_impl/src/UDPConnection.h b/cpp_impl/src/UDPConnection.h index 71717cd..1ae115b 100644 --- a/cpp_impl/src/UDPConnection.h +++ b/cpp_impl/src/UDPConnection.h @@ -52,6 +52,7 @@ typedef enum { SILENT, ERROR, WARNING, VERBOSE, INFO } UDPC_LoggingType; typedef struct { struct in6_addr addr; + uint32_t scope_id; uint16_t port; } UDPC_ConnectionId; @@ -73,6 +74,8 @@ typedef struct { /// port should be in native byte order (not network/big-endian) UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port); +UDPC_ConnectionId UDPC_create_id_full(struct in6_addr addr, uint32_t scope_id, uint16_t port); + UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port); UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient); @@ -102,11 +105,15 @@ UDPC_LoggingType UDPC_set_logging_type(UDPC_HContext ctx, UDPC_LoggingType loggi UDPC_PacketInfo UDPC_get_received(UDPC_HContext ctx); -const char *UDPC_atostr(UDPC_HContext ctx, UDPC_ConnectionId connectionId); +const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId); + +const char *UDPC_atostr(UDPC_HContext ctx, struct in6_addr addr); /// addrStr must be a valid ipv6 address or a valid ipv4 address struct in6_addr UDPC_strtoa(const char *addrStr); +struct in6_addr UDPC_strtoa_link(const char *addrStr, uint32_t *linkId_out); + #ifdef __cplusplus } #endif diff --git a/cpp_impl/src/test/TestUDPC.cpp b/cpp_impl/src/test/TestUDPC.cpp index 779ffd9..34c7689 100644 --- a/cpp_impl/src/test/TestUDPC.cpp +++ b/cpp_impl/src/test/TestUDPC.cpp @@ -15,23 +15,23 @@ TEST(UDPC, atostr) { for(unsigned int i = 0; i < 16; ++i) { conId.addr.s6_addr[i] = (i % 3 == 0 ? 0xFF : (i % 3 == 1 ? 0x12 : 0x56)); } - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "ff12:56ff:1256:ff12:56ff:1256:ff12:56ff"); for(unsigned int i = 0; i < 8; ++i) { conId.addr.s6_addr[i] = 0; } - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "::56ff:1256:ff12:56ff"); conId.addr.s6_addr[0] = 1; conId.addr.s6_addr[1] = 2; - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "12::56ff:1256:ff12:56ff"); conId.addr.s6_addr[14] = 0; conId.addr.s6_addr[15] = 0; - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "12::56ff:1256:ff12:0"); for(unsigned int i = 0; i < 15; ++i) { @@ -39,12 +39,12 @@ TEST(UDPC, atostr) { } conId.addr.s6_addr[15] = 1; - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "::1"); conId.addr.s6_addr[15] = 0; - resultBuf = UDPC_atostr((UDPC_HContext)&context, conId); + resultBuf = UDPC_atostr((UDPC_HContext)&context, conId.addr); EXPECT_STREQ(resultBuf, "::"); } @@ -128,9 +128,10 @@ TEST(UDPC, atostr_concurrent) { 0, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x11, 0x0, (unsigned char)(id + 1)}, + 0, 0 }; - ptr[id] = UDPC_atostr((UDPC_HContext)c, conId); + ptr[id] = UDPC_atostr((UDPC_HContext)c, conId.addr); }, j, ptrs, &context); } for(unsigned int j = 0; j < 32; ++j) { diff --git a/cpp_impl/src/test/UDPC_NetworkTest.cpp b/cpp_impl/src/test/UDPC_NetworkTest.cpp index 416318c..9e617f2 100644 --- a/cpp_impl/src/test/UDPC_NetworkTest.cpp +++ b/cpp_impl/src/test/UDPC_NetworkTest.cpp @@ -3,9 +3,12 @@ #include #include #include +#include #include +static const std::regex ipv6_regex_linkonly = std::regex(R"d(fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,})d"); + void usage() { puts("[-c | -s] - client or server (default server)"); puts("-ll - listen addr"); @@ -75,11 +78,21 @@ int main(int argc, char **argv) { UDPC_ConnectionId connectionId; if(std::strcmp(listenAddr, "any") == 0) { listenId = UDPC_create_id_anyaddr(std::atoi(listenPort)); + } else if(std::regex_match(listenAddr, ipv6_regex_linkonly)) { + uint32_t scope_id; + auto addr = UDPC_strtoa_link(listenAddr, &scope_id); + listenId = UDPC_create_id_full(addr, scope_id, std::atoi(listenPort)); } else { listenId = UDPC_create_id(UDPC_strtoa(listenAddr), std::atoi(listenPort)); } if(isClient) { - connectionId = UDPC_create_id(UDPC_strtoa(connectionAddr), std::atoi(connectionPort)); + if(std::regex_match(connectionAddr, ipv6_regex_linkonly)) { + uint32_t scope_id; + auto addr = UDPC_strtoa_link(connectionAddr, &scope_id); + connectionId = UDPC_create_id_full(addr, scope_id, std::atoi(connectionPort)); + } else { + connectionId = UDPC_create_id(UDPC_strtoa(connectionAddr), std::atoi(connectionPort)); + } } auto context = UDPC_init_threaded_update(listenId, isClient ? 1 : 0); if(!context) {