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); }