map.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * You may redistribute this program and/or modify it under the terms of
  3. * the GNU General Public License as published by the Free Software Foundation,
  4. * either version 3 of the License, or (at your option) any later version.
  5. *
  6. * This program is distributed in the hope that it will be useful,
  7. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. * GNU General Public License for more details.
  10. *
  11. * You should have received a copy of the GNU General Public License
  12. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. */
  14. var w = 800;
  15. var h = 800;
  16. var fill = d3.scale.category10()
  17. var DEBUGGING = false;
  18. /** True if we don't want to map nodes with 0 reach because they might not actually be reachable. */
  19. var EXCLUDE_ZERO_NODES = false;
  20. var vis = d3.select("#viz").append("svg").attr("width", w).attr("height", h);
  21. var uniqueNodes;
  22. var pairs;
  23. var thisNode;
  24. var force;
  25. var doRequest = function(query, callback) {
  26. var url = '/api/?q=' + encodeURIComponent(bencode(query));
  27. var xmlhttp = new XMLHttpRequest();
  28. xmlhttp.open("GET", url, true);
  29. xmlhttp.onreadystatechange = function() {
  30. if (xmlhttp.readyState==4) {
  31. callback(bdecode(xmlhttp.responseText));
  32. }
  33. };
  34. xmlhttp.send(null);
  35. };
  36. var getData = function()
  37. {
  38. var log2 = function(number)
  39. {
  40. var out = 0;
  41. while ((number >>= 1) > 0) {
  42. out++;
  43. }
  44. return out;
  45. };
  46. // return true if a runs through b
  47. var runsThrough = function(a, b)
  48. {
  49. var mask = log2(b);
  50. return (a & mask) == (b & mask);
  51. };
  52. var buildBranch = function(parsedRoutes, indexOfTip)
  53. {
  54. var branch = [];
  55. branch.push(parsedRoutes[indexOfTip]);
  56. var path = parsedRoutes[indexOfTip].path;
  57. for (var i = indexOfTip + 1; i < parsedRoutes.length; i++) {
  58. if (runsThrough(path, parsedRoutes[i].path)) {
  59. branch.push(parsedRoutes[i]);
  60. path = parsedRoutes[i].path;
  61. }
  62. }
  63. return branch;
  64. }
  65. var buildTree = function(parsedRoutes)
  66. {
  67. var tree = [];
  68. for (var i = 0; i < parsedRoutes.length; i++) {
  69. tree.push({
  70. value: parsedRoutes[i].link,
  71. branch: buildBranch(parsedRoutes, i)
  72. });
  73. }
  74. return tree;
  75. };
  76. var parsePath = function(path) {
  77. return new Number('0x' + path.replace(/\./g, ''));
  78. };
  79. var parseRoutes = function(list)
  80. {
  81. var out = [];
  82. for (var i = 0; i < list.length; i++) {
  83. if (EXCLUDE_ZERO_NODES && list[i].link == 0) {
  84. continue;
  85. }
  86. out.push({
  87. name: list[i].ip,
  88. path: parsePath(list[i].path),
  89. link: list[i].link
  90. });
  91. }
  92. out.sort(function(a, b) { return b.path - a.path });
  93. return out;
  94. }
  95. /**
  96. * @param routes the full list of nodes.
  97. * @param nodesByIp an empty hash which will be populated with nodes.
  98. * @return a list of unique nodes.
  99. */
  100. var getUniqueNodes = function(routes, nodesByIp)
  101. {
  102. var out = [];
  103. for (var i = 0; i < routes.length; i++) {
  104. if (!nodesByIp[routes[i].name]) {
  105. out.push(nodesByIp[routes[i].name] = routes[i]);
  106. }
  107. }
  108. return out;
  109. };
  110. var getPairs = function(tree, nodesByIp)
  111. {
  112. var out = [];
  113. var pairs = {};
  114. for (var i = 0; i < tree.length; i++) {
  115. var branch = tree[i];
  116. for (var j = 0; j < branch.branch.length - 1; j++) {
  117. var nodes = [branch.branch[j], branch.branch[j + 1]];
  118. if (nodes[0].name == nodes[1].name) {
  119. continue;
  120. }
  121. nodes.sort(function(a, b) { return a.name.localeCompare(b.name); });
  122. var p1 = pairs[nodes[0].name];
  123. if (!p1) {
  124. p1 = pairs[nodes[0].name] = {};
  125. }
  126. if (!p1[nodes[1].name]) {
  127. var link = {
  128. source: nodesByIp[nodes[0].name],
  129. target: nodesByIp[nodes[1].name],
  130. value: new Number(branch.value)
  131. };
  132. p1[nodes[1].name] = link;
  133. out.push(link);
  134. } else {
  135. p1[nodes[1].name].value += new Number(branch.value);
  136. }
  137. }
  138. }
  139. return out;
  140. };
  141. var genNodeList = function(table) {
  142. var out = '<table border="1">';
  143. out += '<tr class="theader">'
  144. out += '<th>IPv6 Address</th>';
  145. out += '<th>Path</th>';
  146. out += '<th>Link State</th>';
  147. out += '</tr>'
  148. for (var i = 0; i < table.length; i++) {
  149. if (i % 2 == 0)
  150. out += '<tr class="trow1">';
  151. else
  152. out += '<tr class="trow2">';
  153. out += '<td>' + table[i].ip + '</td>';
  154. var p = table[i].path;
  155. out += '<td>'+p+' (<a id="ping-'+p+'" onclick="window.ping(\''+p+'\')" href="#runcmd">ping</a>)</td>';
  156. out += '<td>' + table[i].link + '<br/>';
  157. out += '</tr>'
  158. }
  159. return out;
  160. };
  161. var getTable = function(firstRun, more, start) {
  162. if (start && start > 20000) {
  163. // sanity limit
  164. return;
  165. }
  166. var q = {"q": "NodeStore_dumpTable"};
  167. if (start) {
  168. q.start = start;
  169. }
  170. doRequest(q, function(responseJSON) {
  171. var table = responseJSON.routingTable;
  172. if (more) {
  173. for (var i = 0; i < more.routingTable.length; i++) {
  174. table.push(more.routingTable[i]);
  175. }
  176. }
  177. if (responseJSON.more == 1) {
  178. getTable(firstRun, responseJSON, ((start) ? start : 0) + 500);
  179. return;
  180. }
  181. table.sort(function(a, b) {
  182. if (b.link == a.link) {
  183. return parsePath(a.path) - parsePath(b.path);
  184. }
  185. return b.link - a.link;
  186. });
  187. document.getElementById('nodes-list').innerHTML = genNodeList(table);
  188. var routes = parseRoutes(table);
  189. var tree = buildTree(routes);
  190. var nodeByIp = {};
  191. uniqueNodes = getUniqueNodes(routes, nodeByIp);
  192. thisNode = uniqueNodes[uniqueNodes.length - 1];
  193. thisNode.group = 0;
  194. pairs = getPairs(tree, nodeByIp);
  195. if (firstRun) {
  196. doLayout(1);
  197. } else {
  198. force.start();
  199. }
  200. });
  201. //setTimeout(function() { getTable(false) }, 10000);
  202. };
  203. getTable(1);
  204. var checkMemory = function() {
  205. doRequest({"q":"memory"}, function(responseJSON) {
  206. document.getElementById('memory-bytes').innerText = responseJSON.bytes;
  207. });
  208. setTimeout(checkMemory, 4000);
  209. }
  210. checkMemory();
  211. };
  212. var doAuthedRequest = function(request, password, callback)
  213. {
  214. doRequest({"q":"cookie"}, function(cookieJSON) {
  215. var hashA = SHA256_hash('' + password + cookieJSON.cookie);
  216. request.cookie = cookieJSON.cookie;
  217. request.hash = hashA;
  218. request.aq = request.q;
  219. request.q = 'auth';
  220. var reqStr = bencode(request);
  221. var hashB = SHA256_hash(reqStr);
  222. request.hash = hashB
  223. doRequest(request, callback);
  224. });
  225. };
  226. window.ping = function(node) {
  227. var cmdInput = document.getElementById('runcmd-cmd');
  228. cmdInput.value = '{"q":"RouterModule_pingNode", "args":{"path":"' + node + '"}}';
  229. };
  230. var setupSubmitEvent = function() {
  231. document.removeEventListener('DOMContentLoaded', setupSubmitEvent, false);
  232. document.getElementById('runcmd-submit').addEventListener('mousedown', function(ev) {
  233. ev.stopPropagation();
  234. var cmdInput = document.getElementById('runcmd-cmd').value;
  235. var cmdPasswd = document.getElementById('runcmd-passwd').value;
  236. var output = document.getElementById('runcmd-output');
  237. output.innerText = '';
  238. var query = null;
  239. try {
  240. query = JSON.parse(cmdInput);
  241. } catch (e) {
  242. output.innerText = "failed to parse input\n\n" + JSON.stringify(e);
  243. return;
  244. }
  245. doAuthedRequest(query, cmdPasswd, function(response) {
  246. output.innerText = JSON.stringify(response);
  247. });
  248. }, false);
  249. };
  250. document.addEventListener("DOMContentLoaded", setupSubmitEvent, false);
  251. var doLayout = function(step)
  252. {
  253. if (!step) {
  254. getData();
  255. return;
  256. }
  257. force = d3.layout.force()
  258. .charge(-400)
  259. .linkDistance(100)//function(link){return 100 / (link.value + 1)})
  260. .nodes(uniqueNodes)
  261. .links(pairs)
  262. .size([w, h])
  263. .start();
  264. var link = vis.selectAll("line.link")
  265. .data(pairs)
  266. .enter()
  267. .append("line")
  268. .attr("class", "link")
  269. .style("stroke-width", function(d) { return Math.sqrt(d.value) / 300000; })
  270. .attr("x1", function(d) { return d.source.x; })
  271. .attr("y1", function(d) { return d.source.y; })
  272. .attr("x2", function(d) { return d.target.x; })
  273. .attr("y2", function(d) { return d.target.y; });
  274. var node = vis.selectAll("circle.node")
  275. .data(uniqueNodes)
  276. .enter()
  277. .append("circle")
  278. .attr("class", "node")
  279. .attr("cx", function(d) { return d.x; })
  280. .attr("cy", function(d) { return d.y; })
  281. .attr("r", function(d) { return Math.max(3, Math.min(Math.sqrt(d.link) * 1/4000, 8)) })
  282. .style("fill", function(d) { return fill(d.group); })
  283. .call(force.drag);
  284. node.append("title").text(function(d) { return d.name; });
  285. force.on("tick", function() {
  286. link.attr("x1", function(d) { return d.source.x; })
  287. .attr("y1", function(d) { return d.source.y; })
  288. .attr("x2", function(d) { return d.target.x; })
  289. .attr("y2", function(d) { return d.target.y; });
  290. node.attr("cx", function(d) { return d.x; })
  291. .attr("cy", function(d) { return d.y; });
  292. });
  293. }