Bug 1395914 - Figure out network id for IPv6 too (Linux) r=michal

Differential Revision: https://phabricator.services.mozilla.com/D34903

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Valentin Gosu 2019-06-28 11:42:18 +00:00
Родитель 6d7a4fd701
Коммит 1fc5fb9a40
2 изменённых файлов: 172 добавлений и 25 удалений

50
netwerk/base/IPv6Utils.h Normal file
Просмотреть файл

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NETWORK_IPV6_UTILS_H_
#define NETWORK_IPV6_UTILS_H_
namespace mozilla {
namespace net {
namespace utils {
// IPv6 address scopes.
#define IPV6_SCOPE_GLOBAL 0 // Global scope.
#define IPV6_SCOPE_LINKLOCAL 1 // Link-local scope.
#define IPV6_SCOPE_SITELOCAL 2 // Site-local scope (deprecated).
#define IPV6_SCOPE_UNIQUELOCAL 3 // Unique local
#define IPV6_SCOPE_NODELOCAL 4 // Loopback
// Return the scope of the given address.
static int ipv6_scope(const unsigned char addr[16]) {
const unsigned char* b = addr;
unsigned short w = (unsigned short)((b[0] << 8) | b[1]);
if ((b[0] & 0xFE) == 0xFC) {
return IPV6_SCOPE_UNIQUELOCAL;
}
switch (w & 0xFFC0) {
case 0xFE80:
return IPV6_SCOPE_LINKLOCAL;
case 0xFEC0:
return IPV6_SCOPE_SITELOCAL;
case 0x0000:
w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] | b[10] |
b[11] | b[12] | b[13] | b[14];
if (w || b[15] != 0x01) {
break;
}
return IPV6_SCOPE_NODELOCAL;
default:
break;
}
return IPV6_SCOPE_GLOBAL;
}
} // namespace utils
} // namespace net
} // namespace mozilla
#endif // NETWORK_IPV6_UTILS_H_

Просмотреть файл

@ -25,6 +25,7 @@
#include "mozilla/SHA1.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Telemetry.h"
#include "../../base/IPv6Utils.h"
/* a shorter name that better explains what it does */
#define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x)
@ -88,13 +89,13 @@ nsNotifyAddrListener::GetLinkType(uint32_t* aLinkType) {
}
//
// Figure out the current "network identification" string.
// Figure out the current IPv4 "network identification" string.
//
// It detects the IP of the default gateway in the routing table, then the MAC
// address of that IP in the ARP table before it hashes that string (to avoid
// information leakage).
//
void nsNotifyAddrListener::calculateNetworkId(void) {
static bool ipv4NetworkId(SHA1Sum* sha1) {
const char* kProcRoute = "/proc/net/route"; /* IPv4 routes */
const char* kProcArp = "/proc/net/arp";
bool found = false;
@ -148,28 +149,7 @@ void nsNotifyAddrListener::calculateNetworkId(void) {
if (gw == searchip) {
LOG(("networkid: MAC %s\n", hw));
nsAutoCString mac(hw);
// This 'addition' could potentially be a
// fixed number from the profile or something.
nsAutoCString addition("local-rubbish");
nsAutoCString output;
SHA1Sum sha1;
nsCString combined(mac + addition);
sha1.update(combined.get(), combined.Length());
uint8_t digest[SHA1Sum::kHashSize];
sha1.finish(digest);
nsCString newString(reinterpret_cast<char*>(digest),
SHA1Sum::kHashSize);
nsresult rv = Base64Encode(newString, output);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
LOG(("networkid: id %s\n", output.get()));
if (mNetworkId != output) {
// new id
Telemetry::Accumulate(Telemetry::NETWORK_ID, 1);
mNetworkId = output;
} else {
// same id
Telemetry::Accumulate(Telemetry::NETWORK_ID, 2);
}
sha1->update(mac.get(), mac.Length());
found = true;
break;
}
@ -180,7 +160,124 @@ void nsNotifyAddrListener::calculateNetworkId(void) {
} /* if (farp) */
} /* if (gw) */
} /* if (froute) */
if (!found) {
return found;
}
// Figure out the current IPv6 "network identification" string.
//
static bool ipv6NetworkId(SHA1Sum* sha1) {
bool found = false;
FILE* ifs = fopen("/proc/net/if_inet6", "r");
if (ifs) {
char buffer[512];
char ip6[40];
int devnum;
int preflen;
int scope;
int flags;
char name[40];
char* l = fgets(buffer, sizeof(buffer), ifs);
// 2a001a28120000090000000000000002 02 40 00 80 eth0
// +------------------------------+ ++ ++ ++ ++ ++
// | | | | | |
// 1 2 3 4 5 6
//
// 1. IPv6 address displayed in 32 hexadecimal chars without colons as
// separator
//
// 2. Netlink device number (interface index) in hexadecimal.
//
// 3. Prefix length in hexadecimal number of bits
//
// 4. Scope value (see kernel source include/net/ipv6.h and
// net/ipv6/addrconf.c for more)
//
// 5. Interface flags (see include/linux/rtnetlink.h and net/ipv6/addrconf.c
// for more)
//
// 6. Device name
//
while (l) {
memset(ip6, 0, sizeof(ip6));
if (6 == sscanf(buffer, "%32[0-9a-f] %02x %02x %02x %02x %31s", ip6,
&devnum, &preflen, &scope, &flags, name)) {
unsigned char id6[16];
memset(id6, 0, sizeof(id6));
for (int i = 0; i < 16; i++) {
char buf[3];
buf[0] = ip6[i * 2];
buf[1] = ip6[i * 2 + 1];
buf[2] = 0;
// convert from hex
id6[i] = (unsigned char)strtol(buf, nullptr, 16);
}
if (net::utils::ipv6_scope(id6) == IPV6_SCOPE_GLOBAL) {
unsigned char prefix[16];
memset(prefix, 0, sizeof(prefix));
uint8_t maskit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
int bits = preflen;
for (int i = 0; i < 16; i++) {
uint8_t mask = (bits >= 8) ? 0xff : maskit[bits];
prefix[i] = id6[i] & mask;
bits -= 8;
if (bits <= 0) {
break;
}
}
// We hash the IPv6 prefix and prefix length in order to
// differentiate between networks with a different prefix length
// For example: 2a00:/16 and 2a00:0/32
sha1->update(prefix, 16);
sha1->update(&preflen, sizeof(preflen));
found = true;
LOG(("networkid: found global IPv6 address %s/%d\n", ip6, preflen));
}
}
l = fgets(buffer, sizeof(buffer), ifs);
}
fclose(ifs);
}
return found;
}
// Figure out the "network identification".
//
void nsNotifyAddrListener::calculateNetworkId(void) {
SHA1Sum sha1;
bool found4 = ipv4NetworkId(&sha1);
bool found6 = ipv6NetworkId(&sha1);
if (found4 || found6) {
// This 'addition' could potentially be a fixed number from the
// profile or something.
nsAutoCString addition("local-rubbish");
nsAutoCString output;
sha1.update(addition.get(), addition.Length());
uint8_t digest[SHA1Sum::kHashSize];
sha1.finish(digest);
nsAutoCString newString(reinterpret_cast<char*>(digest),
SHA1Sum::kHashSize);
nsresult rv = Base64Encode(newString, output);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
LOG(("networkid: id %s\n", output.get()));
if (mNetworkId != output) {
// new id
if (found4 && !found6) {
Telemetry::Accumulate(Telemetry::NETWORK_ID, 1); // IPv4 only
} else if (!found4 && found6) {
Telemetry::Accumulate(Telemetry::NETWORK_ID, 3); // IPv6 only
} else {
Telemetry::Accumulate(Telemetry::NETWORK_ID, 4); // Both!
}
mNetworkId = output;
} else {
// same id
Telemetry::Accumulate(Telemetry::NETWORK_ID, 2);
}
} else {
// no id
Telemetry::Accumulate(Telemetry::NETWORK_ID, 0);
}