Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jv convert ipv4 mapped ipv6 to ipv4 #1906

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
12 changes: 11 additions & 1 deletion collector/lib/ConnTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,22 @@ bool ContainsPrivateNetwork(Address::Family family, NRadixTree tree) {
return tree.IsAnyIPNetSubset(family, private_networks_tree) || private_networks_tree.IsAnyIPNetSubset(family, tree);
}

void ConnectionTracker::UpdateConnectionNoLock(const Connection& conn, const ConnStatus& status) {
Connection ipv4Conn = conn.ConvertToIPv4();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ipv4Conn is misleading, as as the result can be v6

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I know. I should have called it something else or left a comment to remind myself to change the name.

EmplaceOrUpdateNoLock(ipv4Conn, status);
}

void ConnectionTracker::UpdateConnection(const Connection& conn, int64_t timestamp, bool added) {
WITH_LOCK(mutex_) {
EmplaceOrUpdateNoLock(conn, ConnStatus(timestamp, added));
}
}

void ConnectionTracker::UpdateEndpointNoLock(const ContainerEndpoint& endpoint, const ConnStatus& status) {
ContainerEndpoint ipv4Endpoint = endpoint.ConvertToIPv4();
EmplaceOrUpdateNoLock(ipv4Endpoint, status);
}

void ConnectionTracker::Update(
const std::vector<Connection>& all_conns,
const std::vector<ContainerEndpoint>& all_listen_endpoints,
Expand All @@ -63,7 +73,7 @@ void ConnectionTracker::Update(

// Insert (or mark as active) all current connections and listen endpoints.
for (const auto& curr_conn : all_conns) {
EmplaceOrUpdateNoLock(curr_conn, new_status);
UpdateConnectionNoLock(curr_conn, new_status);
}
for (const auto& curr_endpoint : all_listen_endpoints) {
EmplaceOrUpdateNoLock(curr_endpoint, new_status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
EmplaceOrUpdateNoLock(curr_endpoint, new_status);
UpdateEndpointNoLock(curr_endpoint, new_status);

Expand Down
2 changes: 2 additions & 0 deletions collector/lib/ConnTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ class CollectorStats;

class ConnectionTracker {
public:
void UpdateConnectionNoLock(const Connection& conn, const ConnStatus& status);
void UpdateConnection(const Connection& conn, int64_t timestamp, bool added);
void UpdateEndpointNoLock(const ContainerEndpoint& endpoint, const ConnStatus& status);
void AddConnection(const Connection& conn, int64_t timestamp) {
Comment on lines +87 to 90
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the NoLock methods in the private scope. I suggest that we move the ones previously laying in the public section as well.

UpdateConnection(conn, timestamp, true);
}
Expand Down
154 changes: 153 additions & 1 deletion collector/lib/NetworkConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,54 @@ class Address {
std::memcpy(data_.data(), data.data(), Length(family));
}

Address(Family family, const std::array<uint64_t, kU64MaxLen>& data) : data_(data), family_(family) {}
// bool IsIPv4MappedIPv6(const Family& family, const std::array<uint64_t, kU64MaxLen>& data) const {
// if (family != Family::IPV6) {
// return false;
// }

// const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data.data());

// // First 80 bits, 10 bytes, should be 0 if this is IPv4-Mapped IPv6
// for (int i = 0; i < 10; i++) {
// if (bytes[i] != 0) {
// return false;
// }
// }

// // Next 2 bytes should be 0xFF if this is IPv4-Mapped IPv6
// if (bytes[10] != 0xFF || bytes[11] != 0xFF) {
// return false;
// }

// return true;
//}

bool IsIPv4MappedIPv6(const Family& family, const std::array<uint64_t, kU64MaxLen>& data) const {
if (family != Family::IPV6) {
return false;
}

// In IPv4-mapped IPv6 the first 80 bits are zeros and the next 16 bits are ones
// data[0] is 64 bits and therefore is all zeros. The next 16 bits which are the
// first 16 bits of data[1] need to be 0. The next 16 bits of data[1] need to be
// ones in order for the data to represent IPv4-mapped IPv6
return data[0] == 0 && (data[1] & 0xFFFFFFFF00000000ULL) == 0x0000FFFF00000000ULL;
}

Address(Family family, const std::array<uint64_t, kU64MaxLen>& data, bool convertIPv4MappedIPv6 = true) {
if (convertIPv4MappedIPv6 && IsIPv4MappedIPv6(family, data)) {
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data.data());
uint32_t ipv4 = htonl(static_cast<uint32_t>(bytes[12]) << 24 |
static_cast<uint32_t>(bytes[13]) << 16 |
static_cast<uint32_t>(bytes[14]) << 8 |
static_cast<uint32_t>(bytes[15]));
std::memcpy(data_.data(), &ipv4, sizeof(ipv4));
family_ = Family::IPV4;
} else {
family_ = family;
data_ = data;
}
}

// Constructs an IPv4 address given a uint32_t containing the IPv4 address in *network* byte order.
explicit Address(uint32_t ipv4) : data_({htonll(static_cast<uint64_t>(ntohl(ipv4)) << 32), 0}), family_(Family::IPV4) {}
Expand All @@ -63,6 +110,29 @@ class Address {
std::memcpy(data_.data(), &ipv6[0], sizeof(data_));
}

bool IsIPv4MappedIPv6(const uint32_t (&ipv6)[4]) const {
//// In IPv4-mapped IPv6 the first 80 bits are zeros and the next 16 bits are ones
//// data[0] is 64 bits and therefore is all zeros. The next 16 bits which are the
//// first 16 bits of data[1] need to be 0. The next 16 bits of data[1] need to be
//// ones in order for the data to represent IPv4-mapped IPv6
return ipv6[0] == 0 && ipv6[1] == 0 && ipv6[2] == 0x0000FFFF;
}

// uint32_t ConvertIPv6DataToIPv4Data(

//// Constructs an IPv6 address from 4 network-encoded uint32_ts, in network order (high to low)
////explicit Address(const uint32_t (&ipv6)[4], bool convertIPv4MappedIPv6 = true) {
// explicit Address(const uint32_t (&ipv6)[4]) {
// static_assert(sizeof(data_) == sizeof(ipv6));
// if (IsIPv4MappedIPv6(ipv6)) {
// family_(Family::IPV4);
// //std::memcpy(data_.data(), &ipv6[0], sizeof(data_));
// } else {
// family_(Family::IPV6);
// std::memcpy(data_.data(), &ipv6[0], sizeof(data_));
// }
// }

// Constructs an IPv6 address from 2 network-encoded uint64_ts, in network order (high, low).
Address(uint64_t ipv6_high, uint64_t ipv6_low) : data_({ipv6_high, ipv6_low}), family_(Family::IPV6) {}

Expand Down Expand Up @@ -137,6 +207,36 @@ class Address {
}
}

bool IsIPv4MappedIPv6() const {
if (family_ != Family::IPV6) {
return false;
}

const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data_.data());

// First 80 bits, 10 bytes, should be 0 if this is IPv4-Mapped IPv6
for (int i = 0; i < 10; i++) {
if (bytes[i] != 0) {
return false;
}
}

// Next 2 bytes should be 0xFF if this is IPv4-Mapped IPv6
if (bytes[10] != 0xFF || bytes[11] != 0xFF) {
return false;
}
Comment on lines +215 to +227
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For performance reasons, what about declaring a global const IPNet with the /96 mapped prefix, and using .Contains(addr) ?


return true;
}

Address ConvertToIPv4() const {
// Extract the last 32 bits of the IPv6 address, which is the IPv4 address part.
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data_.data());

// Last 4 bytes of the IPv6 address hold the IPv4 address
return Address(bytes[12], bytes[13], bytes[14], bytes[15]);
}

private:
friend std::ostream& operator<<(std::ostream& os, const Address& addr) {
int af = (addr.family_ == Family::IPV4) ? AF_INET : AF_INET6;
Expand Down Expand Up @@ -262,6 +362,16 @@ class IPNet {
return address_ > that.address_;
}

bool IsIPv4MappedIPv6() const {
return address_.IsIPv4MappedIPv6();
}

IPNet ConvertToIPv4() const {
Address ipv4Address = address_.ConvertToIPv4();
size_t bits = bits_ - 96; // 96 = 128 - 32
return IPNet(ipv4Address, bits, is_addr_);
}

private:
friend std::ostream& operator<<(std::ostream& os, const IPNet& net) {
return os << net.address_ << "/" << net.bits_;
Expand Down Expand Up @@ -299,6 +409,15 @@ class Endpoint {
return port_ == 0 && network_.IsNull();
}

bool IsIPv4MappedIPv6() const {
return network_.IsIPv4MappedIPv6();
}

Endpoint ConvertToIPv4() const {
IPNet network = network_.ConvertToIPv4();
return Endpoint(network, port_);
}

private:
friend std::ostream& operator<<(std::ostream& os, const Endpoint& ep) {
// This is an individual IP address.
Expand Down Expand Up @@ -358,6 +477,14 @@ class ContainerEndpoint {

size_t Hash() const { return HashAll(container_, endpoint_, l4proto_); }

ContainerEndpoint ConvertToIPv4() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is misleading as it suggests that the result is always IPv4

if (endpoint_.IsIPv4MappedIPv6()) {
Endpoint ep = endpoint_.ConvertToIPv4();
return ContainerEndpoint(container_, ep, l4proto_, originator_);
}
return *this;
}

private:
std::string container_;
Endpoint endpoint_;
Expand Down Expand Up @@ -389,6 +516,31 @@ class Connection {

size_t Hash() const { return HashAll(container_, local_, remote_, flags_); }

Connection ConvertToIPv4() const {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the name to ConvertToIPv4IfNeeded

bool localMapped = local_.IsIPv4MappedIPv6();
bool remoteMapped = remote_.IsIPv4MappedIPv6();

if (!localMapped && !remoteMapped) {
return *this;
}

Endpoint local;
if (localMapped) {
local = local_.ConvertToIPv4();
} else {
local = local_;
}

Endpoint remote;
if (remoteMapped) {
remote = remote_.ConvertToIPv4();
} else {
remote = remote_;
}

return Connection(container_, local, remote, l4proto(), is_server());
}

private:
std::string container_;
Endpoint local_;
Expand Down
Loading