var BIN_PREFIX_LENGTH = 48, // Shift to /48 units
COLOR_PROP = 'ratio128s';
var colorScale = d3.scaleLinear()
.clamp(false)
.range(['darkblue', 'red']);
d3.json('./_data_20150317_v6_aggregate_counts.json', function(prefixes) {
var hostsData = Object.keys(prefixes).map(function(pref) {
var prefix = new Ip.Prefix(pref),
num64s = Math.pow(2, 64 - prefix.cidr);
prefix = shiftPrefix(prefix, BIN_PREFIX_LENGTH - 128);
return {
start: prefix.firstIp().toNum(),
length: Math.max(1, Math.pow(2, 128 - prefix.cidr)),
name: pref,
cnt64s: prefixes[pref]['64_count'],
cnt128s: prefixes[pref].addr_count,
ratio64s: prefixes[pref]['64_count'] / num64s,
ratio128s: prefixes[pref].addr_count / num64s
};
}).sort(function(a, b) {
return d3.descending(a.length, b.length);
});
// Set scale domain
colorScale.domain([
0,
// use 99th percentile to remove outliars
d3.quantile(hostsData.map(function(d) { return d[COLOR_PROP]; }).sort(), 0.99)
]);
// Get the IANA IPv6 unicast data
d3.csv('./iana-ipv6-unicast.csv', function (unicastData) {
var ianaData = unicastData.map(function(row) {
var prefix = new Ip.Prefix(row.Prefix);
prefix = shiftPrefix(prefix, BIN_PREFIX_LENGTH - 128);
return {
start: prefix.firstIp().toNum(),
length: Math.max(1, Math.pow(2, 128 - prefix.cidr)),
name: row.Designation
};
}).filter(function(prefix) {
// Remove reserved placeholders
return prefix.name !== 'IANA';
});
// Combine IANA and hosts data
var hilbertData = ianaData.concat(hostsData);
// Render Hilbert
HilbertChart()
.width(window.innerHeight - 270)
.hilbertOrder(BIN_PREFIX_LENGTH / 2)
.data(hilbertData)
.showValTooltip(false)
.valFormatter(ipFormatter)
.rangeTooltipContent(d => `${d.name}: ${prefixFormatter(d)}`)
.rangeColor(rangeColor)
(document.getElementById("ipv6-chart"))
.focusOn(
// Zoom in on Global Unicast area
shiftIp(new Ip.Addr('2000::'), BIN_PREFIX_LENGTH - 128).toNum(),
Math.pow(2, (BIN_PREFIX_LENGTH - 4))
);
// Render color legend
d3.select('#color-legend').append('svg')
.attr('width', '500px')
.attr('height', '60px')
.call(d3.legendColor()
.labelFormat('.1e')
.orient('horizontal')
.shapeWidth(48)
.cells(10)
.title('# Hosts per /64')
.scale(colorScale)
)
.select('.legendTitle')
// Push title downwards
.attr('y', 10);
});
});
//
function rangeColor(d) {
if (!d.hasOwnProperty(COLOR_PROP)) {
return '#EEEEF9';
}
return colorScale(d[COLOR_PROP]);
}
function ipFormatter(num) {
return shiftIp(new Ip.Addr(num), 128 - BIN_PREFIX_LENGTH).toString();
}
function prefixFormatter(d) {
if (!d.hasOwnProperty('cnt128s')) {
// Not a host block, show IP range
var ipRange = new Ip.Range(d.start, d.start + d.length - 1),
prefixes = ipRange.toPrefixes();
return shiftPrefix(prefixes[0], 128 - BIN_PREFIX_LENGTH).toString();
}
// Host block
return [
'
', d.cnt64s, '/64s',
'
', d.cnt128s, '/128s'
].join(' ');
}
// bits: positive shifts up, negative shifts down
function shiftIp(ip, bits) {
var bin = ip.toBin();
if (bits < 0) {
bin = bin.slice(0, bin.length + bits);
if (!bin.length) bin = '0';
} else {
for (;bits;bits--) { bin += '0'; }
}
return new Ip.Addr(parseInt(bin, 2));
}
function shiftPrefix(pref, bits) {
return new Ip.Prefix(shiftIp(pref.firstIp(), bits), pref.cidr - bits);
}