123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- * 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 <https://www.gnu.org/licenses/>.
- */
- 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 = '<table border="1">';
- out += '<tr class="theader">'
- out += '<th>IPv6 Address</th>';
- out += '<th>Path</th>';
- out += '<th>Link State</th>';
- out += '</tr>'
- for (var i = 0; i < table.length; i++) {
- if (i % 2 == 0)
- out += '<tr class="trow1">';
- else
- out += '<tr class="trow2">';
- out += '<td>' + table[i].ip + '</td>';
- var p = table[i].path;
- out += '<td>'+p+' (<a id="ping-'+p+'" onclick="window.ping(\''+p+'\')" href="#runcmd">ping</a>)</td>';
- out += '<td>' + table[i].link + '<br/>';
- out += '</tr>'
- }
- 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; });
- });
- }
|