Skip to content

Commit

Permalink
[core] Fixed HS IP parsing in v4-v6 connection
Browse files Browse the repository at this point in the history
Co-authored-by: Maxim Sharabayko <maxsharabayko@haivision.com>
Co-authored-by: Mathieu Poux <mathieu.poux@gmail.com>
  • Loading branch information
3 people committed Nov 24, 2020
1 parent 8114620 commit 81b6651
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 78 deletions.
2 changes: 1 addition & 1 deletion srtcore/api.cpp
Expand Up @@ -654,7 +654,7 @@ int CUDTUnited::newConnection(const SRTSOCKET listen, const sockaddr_any& peer,
// - OVERWRITE just the IP address itself by a value taken from piSelfIP
// (the family is used exactly as the one taken from what has been returned
// by getsockaddr)
CIPAddress::pton((ns->m_SelfAddr), ns->m_pUDT->m_piSelfIP, ns->m_SelfAddr.family(), peer);
CIPAddress::pton((ns->m_SelfAddr), ns->m_pUDT->m_piSelfIP, peer);

{
// protect the m_PeerRec structure (and group existence)
Expand Down
207 changes: 133 additions & 74 deletions srtcore/common.cpp
Expand Up @@ -72,6 +72,11 @@ modified by

using namespace srt::sync;

namespace srt_logging {
extern Logger inlog;
}

using namespace srt_logging;

CUDTException::CUDTException(CodeMajor major, CodeMinor minor, int err):
m_iMajor(major),
Expand Down Expand Up @@ -364,7 +369,10 @@ void CIPAddress::ntop(const sockaddr_any& addr, uint32_t ip[4])
{
if (addr.family() == AF_INET)
{
// SRT internal format of IPv4 address.
// The IPv4 address is in the first field. The rest is 0.
ip[0] = addr.sin.sin_addr.s_addr;
ip[1] = ip[2] = ip[3] = 0;
}
else
{
Expand All @@ -376,92 +384,143 @@ void CIPAddress::ntop(const sockaddr_any& addr, uint32_t ip[4])
}
}

bool checkMappedIPv4(const uint16_t* addr)
{
static const uint16_t ipv4on6_model [8] =
{
0, 0, 0, 0, 0, 0xFFFF, 0, 0
};

// Compare only first 6 words. Remaining 2 words
// comprise the IPv4 address, if these first 6 match.
const uint16_t* mbegin = ipv4on6_model;
const uint16_t* mend = ipv4on6_model + 6;

return std::equal(mbegin, mend, addr);
}

// XXX This has void return and the first argument is passed by reference.
// Consider simply returning sockaddr_any by value.
void CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], int agent_family, const sockaddr_any& peer)
void CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const sockaddr_any& peer)
{
if (agent_family == AF_INET)
{
sockaddr_in* a = (&w_addr.sin);
a->sin_addr.s_addr = ip[0];
}
else // AF_INET6
{
// Check if the peer address is a model of IPv4-mapped-on-IPv6.
// If so, it means that the `ip` array should be interpreted as IPv4.
uint32_t* target_ipv4_addr = NULL;

uint16_t* peeraddr16 = (uint16_t*)peer.sin6.sin6_addr.s6_addr;
static const uint16_t ipv4on6_model [8] =
{
0, 0, 0, 0, 0, 0xFFFF, 0, 0
};
if (peer.family() == AF_INET)
{
sockaddr_in* a = (&w_addr.sin);
target_ipv4_addr = (uint32_t*) &a->sin_addr.s_addr;
}
else // AF_INET6
{
// Check if the peer address is a model of IPv4-mapped-on-IPv6.
// If so, it means that the `ip` array should be interpreted as IPv4.
const bool is_mapped_ipv4 = checkMappedIPv4((uint16_t*)peer.sin6.sin6_addr.s6_addr);

sockaddr_in6* a = (&w_addr.sin6);

// This whole above procedure was only in order to EXCLUDE the
// possibility of IPv4-mapped-on-IPv6. This below may only happen
// if BOTH peers are IPv6. Otherwise we have a situation of cross-IP
// version connection in which case the address in question is always
// IPv4 in various mapping formats.
if (!is_mapped_ipv4)
{
// Here both agent and peer use IPv6, in which case
// `ip` contains the full IPv6 address, so just copy
// it as is.

// XXX Possibly, a simple
// memcpy( (a->sin6_addr.s6_addr), ip, 16);
// would do the same thing, and faster. The address in `ip`,
// even though coded here as uint32_t, is still big endian.
for (int i = 0; i < 4; ++ i)
{
a->sin6_addr.s6_addr[i * 4 + 0] = ip[i] & 0xFF;
a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8);
a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16);
a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24);
}
return; // The address is written, nothing left to do.
}

// Compare only first 6 words. Remaining 2 words
// comprise the IPv4 address, if these first 6 match.
const uint16_t* mbegin = ipv4on6_model;
const uint16_t* mend = ipv4on6_model + 6;
//
// IPv4 mapped on IPv6

// Here agent uses IPv6 with IPPROTO_IPV6/IPV6_V6ONLY == 0
// In this case, the address in `ip` is always an IPv4,
// although we are not certain as to whether it's using the
// IPv6 encoding (0::FFFF:IPv4) or SRT encoding (IPv4::0);
// this must be extra determined.
//
// Unfortunately, sockaddr_in6 doesn't give any straightforward
// method for it, although the official size of a single element
// of the IPv6 address is 16-bit.

memset((a->sin6_addr.s6_addr), 0, sizeof a->sin6_addr.s6_addr);

// The sin6_addr.s6_addr32 is non that portable to use here.
uint32_t* paddr32 = (uint32_t*)a->sin6_addr.s6_addr;
uint16_t* paddr16 = (uint16_t*)a->sin6_addr.s6_addr;

// layout: of IPv4 address 192.168.128.2
// 16-bit:
// [0000: 0000: 0000: 0000: 0000: FFFF: 192.168:128.2]
// 8-bit
// [00/00/00/00/00/00/00/00/00/00/FF/FF/192/168/128/2]
// 32-bit
// [00000000 && 00000000 && 0000FFFF && 192.168.128.2]

// Spreading every 16-bit word separately to avoid endian dilemmas
paddr16[2 * 2 + 1] = 0xFFFF;

target_ipv4_addr = &paddr32[3];
}

bool is_mapped_ipv4 = (std::mismatch(mbegin, mend, peeraddr16).first == mend);
// Now we have two possible formats of encoding the IPv4 address:
// 1. If peer is IPv4, it's IPv4::0
// 2. If peer is IPv6, it's 0::FFFF:IPv4.
//
// Has any other possibility happen here, copy an empty address,
// which will be the only sign of an error.

sockaddr_in6* a = (&w_addr.sin6);
const uint16_t* peeraddr16 = (uint16_t*)ip;
const bool is_mapped_ipv4 = checkMappedIPv4(peeraddr16);

if (!is_mapped_ipv4)
{
// Here both agent and peer use IPv6, in which case
// `ip` contains the full IPv6 address, so just copy
// it as is.

// XXX Possibly, a simple
// memcpy( (a->sin6_addr.s6_addr), ip, 16);
// would do the same thing, and faster. The address in `ip`,
// even though coded here as uint32_t, is still big endian.
for (int i = 0; i < 4; ++ i)
{
a->sin6_addr.s6_addr[i * 4 + 0] = ip[i] & 0xFF;
a->sin6_addr.s6_addr[i * 4 + 1] = (unsigned char)((ip[i] & 0xFF00) >> 8);
a->sin6_addr.s6_addr[i * 4 + 2] = (unsigned char)((ip[i] & 0xFF0000) >> 16);
a->sin6_addr.s6_addr[i * 4 + 3] = (unsigned char)((ip[i] & 0xFF000000) >> 24);
}
}
else // IPv4 mapped on IPv6
{
// Here agent uses IPv6 with IPPROTO_IPV6/IPV6_V6ONLY == 0
// In this case, the address in `ip` uses only the `ip[0]`
// element carrying the IPv4 address, rest of the elements
// is 0. This address must be interpreted as IPv4, but set
// as the IPv4-on-IPv6 address.
//
// Unfortunately, sockaddr_in6 doesn't give any straightforward
// method for it, although the official size of a single element
// of the IPv6 address is 16-bit.

memset((a->sin6_addr.s6_addr), 0, sizeof a->sin6_addr.s6_addr);

// There are also available sin6_addr.s6_addr32, but
// this is kinda nonportable.
uint32_t* paddr32 = (uint32_t*)a->sin6_addr.s6_addr;
uint16_t* paddr16 = (uint16_t*)a->sin6_addr.s6_addr;

// layout: of IPv4 address 192.168.128.2
// 16-bit:
// [0000: 0000: 0000: 0000: 0000: FFFF: 192.168:128.2]
// 8-bit
// [00/00/00/00/00/00/00/00/00/00/FF/FF/192/168/128/2]
// 32-bit
// [00000000 && 00000000 && 0000FFFF && 192.168.128.2]

// Spreading every 16-bit word separately to avoid endian dilemmas
paddr16[2 * 2 + 0] = 0;
paddr16[2 * 2 + 1] = 0xFFFF;

paddr32[3] = ip[0]; // IPv4 address encoded here
}
}
if (is_mapped_ipv4)
{
*target_ipv4_addr = ip[3];
HLOGC(inlog.Debug, log << "pton: Handshake address: " << w_addr.str() << " provided in IPv6 mapping format");
}
// Check SRT IPv4 format.
else if ((ip[1] | ip[2] | ip[3]) == 0)
{
*target_ipv4_addr = ip[0];
HLOGC(inlog.Debug, log << "pton: Handshake address: " << w_addr.str() << " provided in SRT IPv4 format");
}
else
{
LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << std::hex
<< peeraddr16[0] << ":"
<< peeraddr16[1] << ":"
<< peeraddr16[2] << ":"
<< peeraddr16[3] << ":"
<< peeraddr16[4] << ":"
<< peeraddr16[5] << ":"
<< peeraddr16[6] << ":"
<< peeraddr16[7] << std::dec);
*target_ipv4_addr = 0;
if (peer.family() != AF_INET)
{
// Additionally overwrite the 0xFFFF that has been
// just written 50 lines above.
w_addr.sin6.sin6_addr.s6_addr[10] = 0;
w_addr.sin6.sin6_addr.s6_addr[11] = 0;
}
}
}

using namespace std;


static string ShowIP4(const sockaddr_in* sin)
{
ostringstream os;
Expand Down
2 changes: 1 addition & 1 deletion srtcore/common.h
Expand Up @@ -835,7 +835,7 @@ struct CIPAddress
{
static bool ipcmp(const struct sockaddr* addr1, const struct sockaddr* addr2, int ver = AF_INET);
static void ntop(const struct sockaddr_any& addr, uint32_t ip[4]);
static void pton(sockaddr_any& addr, const uint32_t ip[4], int sa_family, const sockaddr_any& peer);
static void pton(sockaddr_any& addr, const uint32_t ip[4], const sockaddr_any& peer);
static std::string show(const struct sockaddr* adr);
};

Expand Down
4 changes: 2 additions & 2 deletions srtcore/core.cpp
Expand Up @@ -5107,7 +5107,7 @@ EConnectStatus CUDT::postConnect(const CPacket &response, bool rendezvous, CUDTE
// otherwise if startConnect() fails, the multiplexer cannot be located
// by garbage collection and will cause leak
s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr((s->m_SelfAddr));
CIPAddress::pton((s->m_SelfAddr), s->m_pUDT->m_piSelfIP, s->m_SelfAddr.family(), m_PeerAddr);
CIPAddress::pton((s->m_SelfAddr), s->m_pUDT->m_piSelfIP, m_PeerAddr);

s->m_Status = SRTS_CONNECTED;

Expand Down Expand Up @@ -5903,7 +5903,7 @@ void CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& peer,
// get local IP address and send the peer its IP address (because UDP cannot get local IP address)
memcpy((m_piSelfIP), w_hs.m_piPeerIP, sizeof m_piSelfIP);
m_parent->m_SelfAddr = agent;
CIPAddress::pton((m_parent->m_SelfAddr), m_piSelfIP, agent.family(), peer);
CIPAddress::pton((m_parent->m_SelfAddr), m_piSelfIP, peer);
CIPAddress::ntop(peer, (w_hs.m_piPeerIP));

int udpsize = m_iMSS - CPacket::UDP_HDR_SIZE;
Expand Down

0 comments on commit 81b6651

Please sign in to comment.