8823a36bce2c88efce8550be655e779dc2825a37
[random_node_code.git] / bgpfake / bf2.js
1 var readline = require('readline');
2 var net = require('net');
3
4
5
6 // ---- vars
7
8 var asPaths = new Array();
9 var readyToSend = false;
10 var currentPrompt;
11 var rl;
12 var nCons = 0;
13 var nSent = 0;
14 var myAS;
15 var myIP;
16 var server;
17 var cState = "starting";
18 var currentIPa = 1;
19 var currentIPb = 0;
20 var currentIPc = 0;
21 var timerIntervalObject;
22 var currentCon = 0;
23 var sequentialIPs = true;
24 var usePrivateRanges = false;
25 var randomNextHop = false;
26 var timeBetweenUpdates = 1000;
27 var routesPerUpdate = 100;
28 var updatesPerInterval = 20;
29
30 // ---- vars
31
32
33 if(typeof process.argv[2] == "undefined") {
34         usage();
35 }
36
37 if(typeof process.argv[3] == "undefined") {
38         usage();
39 }
40
41 function usage() {
42         console.log("Usage: "+process.argv[1]+" MyAS MyIP");
43         process.exit(1);
44 }
45
46
47
48
49
50 // ----------- startup
51
52 myAS = process.argv[2];
53 myIP = process.argv[3];
54
55
56 startCLI();
57 doPrompt();
58 createAsPathArray(1048576);
59 startServer();
60 cState = "idle";
61 doPrompt();
62
63 // ----------- startup
64
65
66
67
68
69
70
71
72
73
74 // --------- CLI
75
76 function updatePrompt() {
77         currentPrompt = "("+myAS+"/"+myIP+") "+cState+":"+nCons+"/"+nSent+" ("+currentIPa+"."+currentIPb+"."+currentIPc+") > ";
78 }
79
80 function startCLI() {
81         currentPrompt = "("+myAS+"/"+myIP+") starting... > ";
82
83         rl = readline.createInterface({
84                   input: process.stdin,
85                   output: process.stdout
86                 });
87         
88         rl.on('line', function (cmd) {
89                 switch(cmd) {
90                 
91                 case "?":
92                 case "help":
93                 case "h":
94                         printCLIUsage();
95                         break;
96                 case "r":
97                         currentIPa = 1;
98                         currentIPb = 0;
99                         currentIPc = 0;
100                         break;
101                 case "a":
102                         togglePrivateRange();
103                         break;
104                 case "t":
105                         toggleIPChoice();
106                         break;
107                 case "m":
108                         toggleRandomNextHop();
109                         break;
110                 case "s":
111                         printStatus();
112                         break;
113                 case "u":
114                         startUpdates();
115                         break;
116                 case "p":
117                         stopUpdates();
118                         break;
119                 case "q":
120                 case "quit":
121                 case "end":
122                           rl.close();
123                           process.exit(0);
124                           break;
125                 case "":
126                         break;
127                 }
128                 
129                 doPrompt();
130         });
131 }
132
133
134 function printStatus() {
135         console.log("---- Status ----");
136         console.log("Currently "+cState);
137         console.log("Private ranges: "+usePrivateRanges);
138         console.log("Sequential publication: "+sequentialIPs);
139         console.log("Random NextHop: "+randomNextHop);
140         console.log("Number of connected peers: " + nCons);
141         console.log("Number of routes published: " + nSent);
142         console.log("My IP address: " + myIP);
143         console.log("My ASN: " + myAS);
144         console.log("Current IP (for sequential publications): " + currentIPa + "." + currentIPb + "." + currentIPc + "0/24");
145         console.log("AS path table size: "+asPaths.length);
146 }
147
148 function togglePrivateRange() {
149         if(usePrivateRanges) {
150                 console.log("Switching off private range publication");
151                 usePrivateRanges = false;
152         } else {
153                 console.log("Switching on private range publication");
154                 usePrivateRanges = true;
155         }
156 }
157
158 function toggleIPChoice() {
159         if(sequentialIPs) {
160                 sequentialIPs = false;
161                 console.log("Switching to random IP addresses");
162         } else {
163                 console.log("Switching to sequential IP addresses");
164                 sequentialIPs = true;
165         }
166 }
167
168
169 function toggleRandomNextHop() {
170         if(randomNextHop) {
171                 randomNextHop = false;
172                 console.log("Switching form random next-hop to next-hop-self");
173         } else {
174                 randomNextHop = true;
175                 console.log("Switching form next-hop-self to random next-hop");
176         }
177         
178 }
179
180 function doPrompt() {
181         updatePrompt();
182         rl.setPrompt(currentPrompt);
183         rl.prompt();    
184 }
185
186 function printCLIUsage() {
187         console.log("Help");
188         console.log("\th[elp],? - this help menu");     
189         console.log("\tu - start sending route updates to connected peers");
190         console.log("\tp - pause sending route updates to connected peers");
191         console.log("\ta - toggle use of private ranges");
192         console.log("\tm - toggle between random next hop and my ip as next hop (randomise last octet - assumes /24 on the ip address of this node)");
193         console.log("\ts - status");
194         console.log("\tt - toggles between random and sequential addressing");
195         console.log("\tr - reset IP range back to beginning");
196         console.log("\tq[uit],exit,end - Quit");
197         console.log("Prompt layout");
198         console.log("\t(AS/IP) state:connections/updates-sent (current-route)");
199 }
200
201 function updateState(newstate) {
202         if(cState == newstate) {
203                 doPrompt();
204                 return;
205         }
206         
207         //starting
208         if(newstate == "starting") {
209                 cState = newstate;
210                 doPrompt();
211                 return;
212         }
213         
214         // idle
215         if(newstate == "idle") {
216                 cState = newstate;
217                 doPrompt();
218                 return;
219         }
220
221         // connected
222         if(newstate == "connected") {
223                 cState = newstate;
224                 doPrompt();
225                 return;
226         }
227         
228         // ready
229         if(newstate == "ready") {
230                 if(cState == "sending") return;
231                 cState = newstate;
232                 doPrompt();
233                 return;
234         }
235
236         // sending
237         if(newstate == "sending") {
238                 cState = newstate;
239                 doPrompt();
240                 return;
241         }
242         
243         if(newstate == "stopping") {
244                 cState = "stopping";
245                 doPrompt();
246                 return;
247         }
248
249         
250 }
251
252 // ------------- CLI
253
254
255
256
257
258
259
260
261
262
263
264
265
266 //------------- network
267
268 function startUpdates() {
269         if(cState == "sending") {
270                 console.log("LOG: already sending...");
271                 return;
272         }
273         
274         if(cState != "ready") {
275                 console.log("LOG: not ready to send yet");
276                 return;
277         }
278         
279         
280         // here goes nothing
281         console.log("LOG: starting update sending");
282         updateState("sending");
283         timerIntervalObject = setInterval(sendUpdate, timeBetweenUpdates);
284         //console.log("LOG: stopped sending updates");
285 }
286
287
288 function sendUpdate()
289 {
290         if(cState != "sending") {
291                 console.log("oh, your killing me now?");
292                 clearInterval(timerIntervalObject);
293                 updateState("ready");
294         } else {
295                 for(var i=0; i<updatesPerInterval; i++) {
296                         var msg = constructUpdateMessage(routesPerUpdate);
297                         currentCon.write(msg);
298                 }
299         }
300         
301 }
302
303
304 function stopUpdates() {
305         if(cState != "sending") {
306                 console.log("LOG: not in a sending state, cant pause.");
307                 return;
308         }
309         
310         updateState("stopping");
311 }
312
313 function serverconnection(c) {
314
315         scon = c;
316
317         c.on("end", function() {
318                 //console.log("Server disconnected");
319                 nCons--;
320                 if(nCons < 1) {
321                         cState = "idle";
322                         doPrompt();
323                 }
324                 currentCon = 0;
325         });
326
327         c.on("data", function(buffer) {
328                 parseBuffer(buffer, c);
329         });
330         
331
332         currentCon = c;
333         
334         cState = "connected";
335         nCons++;
336         doPrompt();
337         
338
339
340         console.log("LOG: connection from "+c.remoteAddress);
341         doPrompt();
342
343         //c.write("hello\r\n");
344 }
345
346
347 function startServer() {
348         server = net.createServer(serverconnection);
349
350         server.listen(179, function() {
351                 //console.log("LOG: Server bound");
352                 doPrompt();
353         });
354         
355 }
356
357 //------------- network
358
359
360
361
362
363
364
365
366
367
368 // -------------- BGP 
369
370 function getRandomNextHop() {
371         ipa = 1+Math.round(Math.random()*120);
372         ipb = 1+Math.round(Math.random()*254);
373         ipc = 1+Math.round(Math.random()*254);
374         ipd = 1+Math.round(Math.random()*254);
375         
376         return ipa+"."+ipb+"."+ipc+"."+ipd;
377
378 }
379
380 function getNextIP() {
381         // split into octets
382         //var currentIPa = 1;
383         //var currentIPb = 0;
384         //var currentIPc = 0;
385
386         
387         if(sequentialIPs) {
388                 currentIPc++;
389                 if(currentIPc > 254) {
390                         
391                         currentIPb++;
392                         currentIPc = 0;
393                         if(!usePrivateRanges) if(currentIPb == 168 && currentIPa == 192) currentIPb++;
394                         if(currentIPb > 254) {
395                                 currentIPa++;
396                                 currentIPb = 0;
397                                 
398                                 // dont publish bogons or 127
399                                 if(!usePrivateRanges) {
400                                         if(currentIPa == 10) currentIPa++;
401                                         if(currentIPa == 127) currentIPa++;
402                                         if(currentIPa == 128) currentIPa++;                     
403                                         if(currentIPa == 172) currentIPa++;
404                                 }
405                         }
406                 }
407                 
408                 if(currentIPa > 223) {
409                         console.log("LOG: hit the end of the range, wrapping");
410                         currentIPa = 1;
411                         currentIPb = 0;
412                         currentIPc = 0;
413                 }
414                 
415                 //console.log("created "+a+"."+b+"."+c+" from "+i);
416                 return currentIPa+"."+currentIPb+"."+currentIPc;
417         } else {
418                 ipa = 1+Math.round(Math.random()*223);
419                 ipb = 1+Math.round(Math.random()*254);
420                 ipc = 1+Math.round(Math.random()*254);
421                 
422                 
423                 // TODO remove this
424                 ipa = 1;
425                 ipb = 1;
426                 
427                 if(!usePrivateRanges) {
428                         if(ipb == 168 && ipa == 192) ipb++;
429                         if(ipa == 10) ipa++;
430                         if(ipa == 127) ipa++;
431                         if(ipa == 128) ipa++;                   
432                         if(ipa == 172) ipa++;
433                 }                       
434
435                 return ipa+"."+ipb+"."+ipc;
436         }
437
438 }
439
440 function getASPath() {
441         var n = Math.random();
442         
443         return asPaths[Math.round(asPaths.length*n)];
444 }
445
446 function constructUpdateMessage(n_up) {
447         var bsize = 0;
448
449         var aspath = getASPath();
450         //console.log("aspath is");
451         //console.log(aspath);
452         
453         // first the header components
454         bsize += 16;
455
456         // next the length component
457         bsize += 2;
458
459         // next the n unfeasible
460         bsize += 2;
461
462         // next, path attr length
463         bsize += 2;
464
465
466         // now we begin the path attrs
467         // first origin - simple
468         var aspathn = 4;
469
470         // next as path - hard, flag + type + len + aspath segment
471         aspathn += 3;
472
473         // as path segment size = 1 (type), + 1 (len) + as's*2
474         var aspathlen = ((aspath.length+1)*2)+1+1;
475         aspathn += aspathlen;
476         
477         // now next hop attrs = flag (1) + type (1) + len (1) + octets (4);
478         aspathn += 7;
479         bsize += aspathn;
480
481         // now nlri = prefix len (1) + prefix fixed in our case (3)
482         bsize += 4*n_up;
483
484         // fudge
485         bsize+=1;
486
487         //console.log("size: " + bsize + ", an: " + aspathn + " al:" + aspathlen);
488         var buf = new Buffer(bsize);
489         var bp = 0;
490
491         // now lets create the buffer
492         buf.fill(0xff, bp, bp+16);
493         bp+=16;
494         buf.writeUInt16BE(bsize, bp);
495         bp+=2;
496         buf.writeUInt8(2, bp);
497         bp++;
498         buf.writeUInt16BE(0, bp);
499         bp+=2;
500         buf.writeUInt16BE(aspathn, bp);
501         bp+=2;
502
503         // path attr
504         // origin
505         buf.writeUInt8(0x40, bp);
506         bp++;
507         buf.writeUInt8(1, bp);
508         bp++;
509         buf.writeUInt8(1, bp);
510         bp++;
511         buf.writeUInt8(0, bp);
512         bp++;
513
514         // as path
515         buf.writeUInt8(0x40, bp);
516         bp++;
517         buf.writeUInt8(2, bp);
518         bp++;
519         buf.writeUInt8(aspathlen, bp);
520         bp++;
521         buf.writeUInt8(2, bp);
522         bp++;
523         buf.writeUInt8(aspath.length+1, bp);
524         bp++;
525         //console.log("writing in my aspath: "+myas);
526         buf.writeUInt16BE(myAS, bp);
527         bp+=2;
528         aspath.forEach(function (ed) {
529                 //console.log("writing in aspath: "+ed);
530                 buf.writeUInt16BE(ed, bp);
531                 bp+=2;
532         });
533
534         // next hop
535         buf.writeUInt8(0x40, bp);
536         bp++;
537         buf.writeUInt8(3, bp);
538         bp++;
539         buf.writeUInt8(4, bp);
540         bp++;
541         
542 //      if(randomNextHop) {
543 //              rnh = getRandomNextHop();
544 //              rnh.split(".").forEach(function (ed) {
545 //                      //console.log("writing in next hop info: " + ed);
546 //                      buf.writeUInt8(parseInt(ed), bp);
547 //                      bp++;
548 //              });
549         myIP.split(".").forEach(function (ed) {
550                 //console.log("writing in next hop info: " + ed);
551                 buf.writeUInt8(parseInt(ed), bp);
552                 bp++;
553         });
554         
555         if(randomNextHop) {
556                 nhns = Math.round(1+(Math.random()*250));
557                 bp--
558                 buf.writeUInt8(nhns, bp);
559                 bp++;
560         }
561
562         // last, nlri
563         for(var nn=0; nn < n_up; nn++) {
564                 //console.log("bsize: "+bsize+" bp "+bp);
565                 buf.writeUInt8(24, bp);
566                 bp++;
567                 var ip = getNextIP();
568                 ip.split(".").forEach(function(ed){
569                         //console.log("Writing in nlri: "+ed);
570                         buf.writeUInt8(parseInt(ed), bp);
571                         bp++;
572                 });
573         }
574         
575         
576         nSent += n_up;
577
578         //console.log("buf is:");
579         //console.log(buf);
580         //console.log(buf.length);
581
582         return buf;
583 }
584
585 function createAsPathArray(size) {
586         for(var i=0; i<size; i++) {
587                 asPaths[i] = createaspath(i);
588         }
589 }
590
591
592 function createaspath(i) {
593         var n=(i%5)+2;
594         var as = 1024;
595         var ret = new Array();
596
597         for(var t=0; t<n; t++) {
598                 i = i << 1;
599                 as = 1024 + (i%30000);
600                 ret[t] = as;
601         }
602         return ret;
603 }
604
605 function parseBuffer(b, c) {
606         var len = b.readUInt16BE(16);
607         var type = b.readUInt8(18);
608
609         //console.log("got input: " + len + ", type: " + type);
610
611         if(type == 1) {
612                 var vers = b.readUInt8(19);
613                 var as = b.readUInt16BE(20);
614                 var ht = b.readUInt16BE(22);
615                 var ot1 = b.readUInt8(24);
616                 var ot2 = b.readUInt8(25);
617                 var ot3 = b.readUInt8(26);
618                 var ot4 = b.readUInt8(27);
619                 var opl = b.readUInt8(28);
620                 //console.log("got open type, vers: "+vers+", as: " + as);
621                 //console.log("ht: " + ht + ", id: "+ot1+"."+ot2+"."+ot3+"."+ot4+", opl: "+opl);
622
623
624                 //console.log("sending our open type");
625                 var out = new Buffer(29);
626
627
628                 out.fill(0xff, 0, 16);
629                 out.writeUInt16BE(29, 16);
630                 out.writeUInt8(1, 18);
631                 out.writeUInt8(4, 19);
632                 out.writeUInt16BE(myAS, 20);
633                 out.writeUInt16BE(90, 22);
634                 out.writeUInt8(10, 24);
635                 out.writeUInt8(99, 25);
636                 out.writeUInt8(99, 26);
637                 out.writeUInt8(1,27);
638                 out.writeUInt8(0,28);
639
640                 c.write(out);
641         } else if(type == 4) {
642                 //console.log("writing keepalive - exact as sent");
643                 console.log("LOG: keepalive from remote ("+c.remoteAddress+")");
644                 c.write(b);
645                 readyToSend = true;
646                 updateState("ready");
647                 //if(updateSent ==0) beginUpdateSend(c);
648         } else if(type == 2) {
649                 //console.log("got update...");
650                 console.log("LOG: update from remote ("+c.remoteAddress+")");
651                 readyToSend = true;
652                 updateState("ready");
653         } else if(type == 3) {
654                 var loc = b.readUInt8(19);
655                 var msg = b.readUInt8(20);
656                 var fromremote = parseNotifyMessage(loc, msg);
657                 console.log("LOG: Notification message from server ("+loc+"/"+msg+"): " + fromremote);
658         } else {
659                 //console.log("sending end...");
660                 c.end();
661         }
662
663         doPrompt();
664         
665 }
666
667 function parseNotifyMessage(loc, msg) {
668         var retmsg = "";
669         switch(loc) {
670                 case 1:
671                         retmsg += "Header Error - ";
672                         if(msg == 1) retmsg += "Not Synchronised";
673                         else if(msg == 2) retmsg += "Bad Message Length";
674                         else if(msg == 3) retmsg += "Bad Message Type";
675                         else retmsg += "Unknown error code: "+msg;
676                         break;
677                 case 2:
678                         retmsg += "Open Error - ";
679                         if(msg == 1) retmsg += "Unsupported Version";
680                         else if(msg == 2) retmsg += "AS Missmatch";
681                         else if(msg == 3) retmsg += "Bad BGP ID";
682                         else if(msg == 4) retmsg += "Unsupported Option Parameter";
683                         else retmsg += "Unknown error code: "+msg;
684                         break;
685                 case 3:
686                         retmsg += "Update Error - ";
687                         if(msg == 1) retmsg += "Malformed attribute list";
688                         else if(msg == 2) retmsg += "Unknown recognised well-known attribute";
689                         else if(msg == 3) retmsg += "Missing well-known attribute";
690                         else if(msg == 4) retmsg += "Attribute flag error";
691                         else if(msg == 5) retmsg += "Attribute length error";
692                         else if(msg == 6) retmsg += "Invalid origin attribute";
693                         else if(msg == 7) retmsg += "Deprecated error message (other end is waaay too old)";
694                         else if(msg == 8) retmsg += "Invalid next hop attribute";
695                         else if(msg == 9) retmsg += "Optional attribute error";
696                         else if(msg == 10) retmsg += "Invalid network field";
697                         else if(msg == 11) retmsg += "Malformed AS path";
698                         else retmsg += "Unknown error code: "+msg;
699                         break;
700                 case 4:
701                         retmsg += "Hold Timer Expired - ";
702                         break;
703                 case 5:
704                         retmsg += "Finite State Machine error - "+msg;
705                         break;
706                 default:
707                         retmsg += "Unknown erorr type - "+msg; 
708         }
709         
710         return retmsg;
711         
712 }
713
714 //-------------- BGP