b9c022024de68836ed9ee0f273f0bdcb57c24bcf
[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 = 20;
27 var routesPerUpdate = 100;
28 var updatesPerInterval = 40;
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                 if(!usePrivateRanges) {
424                         if(ipb == 168 && ipa == 192) ipb++;
425                         if(ipa == 10) ipa++;
426                         if(ipa == 127) ipa++;
427                         if(ipa == 128) ipa++;                   
428                         if(ipa == 172) ipa++;
429                 }                       
430
431                 return ipa+"."+ipb+"."+ipc;
432         }
433
434 }
435
436 function getASPath() {
437         var n = Math.random();
438         
439         return asPaths[Math.round(asPaths.length*n)];
440 }
441
442 function constructUpdateMessage(n_up) {
443         var bsize = 0;
444
445         var aspath = getASPath();
446         //console.log("aspath is");
447         //console.log(aspath);
448         
449         // first the header components
450         bsize += 16;
451
452         // next the length component
453         bsize += 2;
454
455         // next the n unfeasible
456         bsize += 2;
457
458         // next, path attr length
459         bsize += 2;
460
461
462         // now we begin the path attrs
463         // first origin - simple
464         var aspathn = 4;
465
466         // next as path - hard, flag + type + len + aspath segment
467         aspathn += 3;
468
469         // as path segment size = 1 (type), + 1 (len) + as's*2
470         var aspathlen = ((aspath.length+1)*2)+1+1;
471         aspathn += aspathlen;
472         
473         // now next hop attrs = flag (1) + type (1) + len (1) + octets (4);
474         aspathn += 7;
475         bsize += aspathn;
476
477         // now nlri = prefix len (1) + prefix fixed in our case (3)
478         bsize += 4*n_up;
479
480         // fudge
481         bsize+=1;
482
483         //console.log("size: " + bsize + ", an: " + aspathn + " al:" + aspathlen);
484         var buf = new Buffer(bsize);
485         var bp = 0;
486
487         // now lets create the buffer
488         buf.fill(0xff, bp, bp+16);
489         bp+=16;
490         buf.writeUInt16BE(bsize, bp);
491         bp+=2;
492         buf.writeUInt8(2, bp);
493         bp++;
494         buf.writeUInt16BE(0, bp);
495         bp+=2;
496         buf.writeUInt16BE(aspathn, bp);
497         bp+=2;
498
499         // path attr
500         // origin
501         buf.writeUInt8(0x40, bp);
502         bp++;
503         buf.writeUInt8(1, bp);
504         bp++;
505         buf.writeUInt8(1, bp);
506         bp++;
507         buf.writeUInt8(0, bp);
508         bp++;
509
510         // as path
511         buf.writeUInt8(0x40, bp);
512         bp++;
513         buf.writeUInt8(2, bp);
514         bp++;
515         buf.writeUInt8(aspathlen, bp);
516         bp++;
517         buf.writeUInt8(2, bp);
518         bp++;
519         buf.writeUInt8(aspath.length+1, bp);
520         bp++;
521         //console.log("writing in my aspath: "+myas);
522         buf.writeUInt16BE(myAS, bp);
523         bp+=2;
524         aspath.forEach(function (ed) {
525                 //console.log("writing in aspath: "+ed);
526                 buf.writeUInt16BE(ed, bp);
527                 bp+=2;
528         });
529
530         // next hop
531         buf.writeUInt8(0x40, bp);
532         bp++;
533         buf.writeUInt8(3, bp);
534         bp++;
535         buf.writeUInt8(4, bp);
536         bp++;
537         
538 //      if(randomNextHop) {
539 //              rnh = getRandomNextHop();
540 //              rnh.split(".").forEach(function (ed) {
541 //                      //console.log("writing in next hop info: " + ed);
542 //                      buf.writeUInt8(parseInt(ed), bp);
543 //                      bp++;
544 //              });
545         myIP.split(".").forEach(function (ed) {
546                 //console.log("writing in next hop info: " + ed);
547                 buf.writeUInt8(parseInt(ed), bp);
548                 bp++;
549         });
550         
551         if(randomNextHop) {
552                 nhns = Math.round(1+(Math.random()*250));
553                 bp--
554                 buf.writeUInt8(nhns, bp);
555                 bp++;
556         }
557
558         // last, nlri
559         for(var nn=0; nn < n_up; nn++) {
560                 //console.log("bsize: "+bsize+" bp "+bp);
561                 buf.writeUInt8(24, bp);
562                 bp++;
563                 var ip = getNextIP();
564                 ip.split(".").forEach(function(ed){
565                         //console.log("Writing in nlri: "+ed);
566                         buf.writeUInt8(parseInt(ed), bp);
567                         bp++;
568                 });
569         }
570         
571         
572         nSent += n_up;
573
574         //console.log("buf is:");
575         //console.log(buf);
576         //console.log(buf.length);
577
578         return buf;
579 }
580
581 function createAsPathArray(size) {
582         for(var i=0; i<size; i++) {
583                 asPaths[i] = createaspath(i);
584         }
585 }
586
587
588 function createaspath(i) {
589         var n=(i%5)+2;
590         var as = 1024;
591         var ret = new Array();
592
593         for(var t=0; t<n; t++) {
594                 i = i << 1;
595                 as = 1024 + (i%30000);
596                 ret[t] = as;
597         }
598         return ret;
599 }
600
601 function parseBuffer(b, c) {
602         var len = b.readUInt16BE(16);
603         var type = b.readUInt8(18);
604
605         //console.log("got input: " + len + ", type: " + type);
606
607         if(type == 1) {
608                 var vers = b.readUInt8(19);
609                 var as = b.readUInt16BE(20);
610                 var ht = b.readUInt16BE(22);
611                 var ot1 = b.readUInt8(24);
612                 var ot2 = b.readUInt8(25);
613                 var ot3 = b.readUInt8(26);
614                 var ot4 = b.readUInt8(27);
615                 var opl = b.readUInt8(28);
616                 //console.log("got open type, vers: "+vers+", as: " + as);
617                 //console.log("ht: " + ht + ", id: "+ot1+"."+ot2+"."+ot3+"."+ot4+", opl: "+opl);
618
619
620                 //console.log("sending our open type");
621                 var out = new Buffer(29);
622
623
624                 out.fill(0xff, 0, 16);
625                 out.writeUInt16BE(29, 16);
626                 out.writeUInt8(1, 18);
627                 out.writeUInt8(4, 19);
628                 out.writeUInt16BE(myAS, 20);
629                 out.writeUInt16BE(90, 22);
630                 out.writeUInt8(10, 24);
631                 out.writeUInt8(99, 25);
632                 out.writeUInt8(99, 26);
633                 out.writeUInt8(1,27);
634                 out.writeUInt8(0,28);
635
636                 c.write(out);
637         } else if(type == 4) {
638                 //console.log("writing keepalive - exact as sent");
639                 console.log("LOG: keepalive from remote ("+c.remoteAddress+")");
640                 c.write(b);
641                 readyToSend = true;
642                 updateState("ready");
643                 //if(updateSent ==0) beginUpdateSend(c);
644         } else if(type == 2) {
645                 //console.log("got update...");
646                 console.log("LOG: update from remote ("+c.remoteAddress+")");
647                 readyToSend = true;
648                 updateState("ready");
649         } else if(type == 3) {
650                 var loc = b.readUInt8(19);
651                 var msg = b.readUInt8(20);
652                 var fromremote = parseNotifyMessage(loc, msg);
653                 console.log("LOG: Notification message from server ("+loc+"/"+msg+"): " + fromremote);
654         } else {
655                 //console.log("sending end...");
656                 c.end();
657         }
658
659         doPrompt();
660         
661 }
662
663 function parseNotifyMessage(loc, msg) {
664         var retmsg = "";
665         switch(loc) {
666                 case 1:
667                         retmsg += "Header Error - ";
668                         if(msg == 1) retmsg += "Not Synchronised";
669                         else if(msg == 2) retmsg += "Bad Message Length";
670                         else if(msg == 3) retmsg += "Bad Message Type";
671                         else retmsg += "Unknown error code: "+msg;
672                         break;
673                 case 2:
674                         retmsg += "Open Error - ";
675                         if(msg == 1) retmsg += "Unsupported Version";
676                         else if(msg == 2) retmsg += "AS Missmatch";
677                         else if(msg == 3) retmsg += "Bad BGP ID";
678                         else if(msg == 4) retmsg += "Unsupported Option Parameter";
679                         else retmsg += "Unknown error code: "+msg;
680                         break;
681                 case 3:
682                         retmsg += "Update Error - ";
683                         if(msg == 1) retmsg += "Malformed attribute list";
684                         else if(msg == 2) retmsg += "Unknown recognised well-known attribute";
685                         else if(msg == 3) retmsg += "Missing well-known attribute";
686                         else if(msg == 4) retmsg += "Attribute flag error";
687                         else if(msg == 5) retmsg += "Attribute length error";
688                         else if(msg == 6) retmsg += "Invalid origin attribute";
689                         else if(msg == 7) retmsg += "Deprecated error message (other end is waaay too old)";
690                         else if(msg == 8) retmsg += "Invalid next hop attribute";
691                         else if(msg == 9) retmsg += "Optional attribute error";
692                         else if(msg == 10) retmsg += "Invalid network field";
693                         else if(msg == 11) retmsg += "Malformed AS path";
694                         else retmsg += "Unknown error code: "+msg;
695                         break;
696                 case 4:
697                         retmsg += "Hold Timer Expired - ";
698                         break;
699                 case 5:
700                         retmsg += "Finite State Machine error - "+msg;
701                         break;
702                 default:
703                         retmsg += "Unknown erorr type - "+msg; 
704         }
705         
706         return retmsg;
707         
708 }
709
710 //-------------- BGP