/* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var w = 800; var h = 800; var fill = d3.scale.category10() var DEBUGGING = false; /** True if we don't want to map nodes with 0 reach because they might not actually be reachable. */ var EXCLUDE_ZERO_NODES = false; var vis = d3.select("#viz").append("svg").attr("width", w).attr("height", h); var uniqueNodes; var pairs; var thisNode; var force; var doRequest = function(query, callback) { var url = '/api/?q=' + encodeURIComponent(bencode(query)); var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url, true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState==4) { callback(bdecode(xmlhttp.responseText)); } }; xmlhttp.send(null); }; var getData = function() { var log2 = function(number) { var out = 0; while ((number >>= 1) > 0) { out++; } return out; }; // return true if a runs through b var runsThrough = function(a, b) { var mask = log2(b); return (a & mask) == (b & mask); }; var buildBranch = function(parsedRoutes, indexOfTip) { var branch = []; branch.push(parsedRoutes[indexOfTip]); var path = parsedRoutes[indexOfTip].path; for (var i = indexOfTip + 1; i < parsedRoutes.length; i++) { if (runsThrough(path, parsedRoutes[i].path)) { branch.push(parsedRoutes[i]); path = parsedRoutes[i].path; } } return branch; } var buildTree = function(parsedRoutes) { var tree = []; for (var i = 0; i < parsedRoutes.length; i++) { tree.push({ value: parsedRoutes[i].link, branch: buildBranch(parsedRoutes, i) }); } return tree; }; var parsePath = function(path) { return new Number('0x' + path.replace(/\./g, '')); }; var parseRoutes = function(list) { var out = []; for (var i = 0; i < list.length; i++) { if (EXCLUDE_ZERO_NODES && list[i].link == 0) { continue; } out.push({ name: list[i].ip, path: parsePath(list[i].path), link: list[i].link }); } out.sort(function(a, b) { return b.path - a.path }); return out; } /** * @param routes the full list of nodes. * @param nodesByIp an empty hash which will be populated with nodes. * @return a list of unique nodes. */ var getUniqueNodes = function(routes, nodesByIp) { var out = []; for (var i = 0; i < routes.length; i++) { if (!nodesByIp[routes[i].name]) { out.push(nodesByIp[routes[i].name] = routes[i]); } } return out; }; var getPairs = function(tree, nodesByIp) { var out = []; var pairs = {}; for (var i = 0; i < tree.length; i++) { var branch = tree[i]; for (var j = 0; j < branch.branch.length - 1; j++) { var nodes = [branch.branch[j], branch.branch[j + 1]]; if (nodes[0].name == nodes[1].name) { continue; } nodes.sort(function(a, b) { return a.name.localeCompare(b.name); }); var p1 = pairs[nodes[0].name]; if (!p1) { p1 = pairs[nodes[0].name] = {}; } if (!p1[nodes[1].name]) { var link = { source: nodesByIp[nodes[0].name], target: nodesByIp[nodes[1].name], value: new Number(branch.value) }; p1[nodes[1].name] = link; out.push(link); } else { p1[nodes[1].name].value += new Number(branch.value); } } } return out; }; var genNodeList = function(table) { var out = ''; out += '' out += ''; out += ''; out += ''; out += '' for (var i = 0; i < table.length; i++) { if (i % 2 == 0) out += ''; else out += ''; out += ''; var p = table[i].path; out += ''; out += '' } return out; }; var getTable = function(firstRun, more, start) { if (start && start > 20000) { // sanity limit return; } var q = {"q": "NodeStore_dumpTable"}; if (start) { q.start = start; } doRequest(q, function(responseJSON) { var table = responseJSON.routingTable; if (more) { for (var i = 0; i < more.routingTable.length; i++) { table.push(more.routingTable[i]); } } if (responseJSON.more == 1) { getTable(firstRun, responseJSON, ((start) ? start : 0) + 500); return; } table.sort(function(a, b) { if (b.link == a.link) { return parsePath(a.path) - parsePath(b.path); } return b.link - a.link; }); document.getElementById('nodes-list').innerHTML = genNodeList(table); var routes = parseRoutes(table); var tree = buildTree(routes); var nodeByIp = {}; uniqueNodes = getUniqueNodes(routes, nodeByIp); thisNode = uniqueNodes[uniqueNodes.length - 1]; thisNode.group = 0; pairs = getPairs(tree, nodeByIp); if (firstRun) { doLayout(1); } else { force.start(); } }); //setTimeout(function() { getTable(false) }, 10000); }; getTable(1); var checkMemory = function() { doRequest({"q":"memory"}, function(responseJSON) { document.getElementById('memory-bytes').innerText = responseJSON.bytes; }); setTimeout(checkMemory, 4000); } checkMemory(); }; var doAuthedRequest = function(request, password, callback) { doRequest({"q":"cookie"}, function(cookieJSON) { var hashA = SHA256_hash('' + password + cookieJSON.cookie); request.cookie = cookieJSON.cookie; request.hash = hashA; request.aq = request.q; request.q = 'auth'; var reqStr = bencode(request); var hashB = SHA256_hash(reqStr); request.hash = hashB doRequest(request, callback); }); }; window.ping = function(node) { var cmdInput = document.getElementById('runcmd-cmd'); cmdInput.value = '{"q":"RouterModule_pingNode", "args":{"path":"' + node + '"}}'; }; var setupSubmitEvent = function() { document.removeEventListener('DOMContentLoaded', setupSubmitEvent, false); document.getElementById('runcmd-submit').addEventListener('mousedown', function(ev) { ev.stopPropagation(); var cmdInput = document.getElementById('runcmd-cmd').value; var cmdPasswd = document.getElementById('runcmd-passwd').value; var output = document.getElementById('runcmd-output'); output.innerText = ''; var query = null; try { query = JSON.parse(cmdInput); } catch (e) { output.innerText = "failed to parse input\n\n" + JSON.stringify(e); return; } doAuthedRequest(query, cmdPasswd, function(response) { output.innerText = JSON.stringify(response); }); }, false); }; document.addEventListener("DOMContentLoaded", setupSubmitEvent, false); var doLayout = function(step) { if (!step) { getData(); return; } force = d3.layout.force() .charge(-400) .linkDistance(100)//function(link){return 100 / (link.value + 1)}) .nodes(uniqueNodes) .links(pairs) .size([w, h]) .start(); var link = vis.selectAll("line.link") .data(pairs) .enter() .append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.value) / 300000; }) .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); var node = vis.selectAll("circle.node") .data(uniqueNodes) .enter() .append("circle") .attr("class", "node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("r", function(d) { return Math.max(3, Math.min(Math.sqrt(d.link) * 1/4000, 8)) }) .style("fill", function(d) { return fill(d.group); }) .call(force.drag); node.append("title").text(function(d) { return d.name; }); force.on("tick", function() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); }); }
IPv6 AddressPathLink State
' + table[i].ip + ''+p+' (ping)' + table[i].link + '
'; out += '