-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhost.cpp
More file actions
1546 lines (1187 loc) · 48.5 KB
/
host.cpp
File metadata and controls
1546 lines (1187 loc) · 48.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <stdlib.h>
#include <iostream> // printing
#include <vector>
#include <thread> // threading
#include <cmath>
#include <algorithm>
#include <unistd.h>
#include <cstdio> // HOST_HOST_EOF
#include <string>
#include <chrono> // fine grained timing
#include <fstream>
#include <iomanip>
#include <cstdint>
#include "host.h"
#include "frame_generators.h"
#include "networking_devices.h"
#include "pdu.h"
#include "data_links.h"
#include "addressing.h"
#include "l3_protocols.h"
#include "l4_protocols.h"
#include "md5.h"
using namespace std;
#define POISSON 10 // the default lambda for poisson random traffic
#define MTU 1500 // the maximum transmission unit describes how long a frame can be
#define FRAME_SIZE_MIN 60
#define HOST_EOF 0xFF
#define DEFAULT_TIMEOUT 1
// #define DEBUG
// #define L4_DEBUG
// #define DHCP_DEBUG
// #define ARP_DEBUG
// #define TEST_SENDING
// #define FRAME_TIMES
#define NUMBER_OF_FRAMES 100 // the number of frames to be sent by the host in a test
Host::Host() {
rx_frame_count = 0;
frame_generator = new Poisson(POISSON);
host_start_time = chrono::high_resolution_clock::now();
name = "unnamed";
frame_queue = new wqueue<MPDU*>;
#ifdef DEBUG
host_print("Online");
#endif
}
Host::Host(vector<uint8_t> mac_addr, string hostname, mutex* stop_m) {
srand (time(NULL));
mac = mac_addr;
rx_frame_count = 0;
name = hostname;
m_mutex = stop_m;
frame_generator = new Poisson(POISSON);
host_start_time = chrono::high_resolution_clock::now();
frame_queue = new wqueue<MPDU*>;
#ifdef DEBUG
host_print("Online with MAC " + mac_to_string(mac));
#endif
}
Host::~Host() {
delete frame_generator;
delete frame_queue;
}
void Host::arp_test(uint32_t dest_ip, int number_of_arps) {
// make the threads for receiving and demuxing/sending
thread receiver_thread(&Host::receiver,this);
thread mux_demux_thread(&Host::demuxer,this);
// get an IP
DHCP_client();
// send some arps
if (!is_broadcast(dest_ip)) {
#ifdef DEBUG
host_print("ARPing");
#endif
arping(dest_ip,number_of_arps,DEFAULT_TIMEOUT);
}
// let them run
receiver_thread.join();
mux_demux_thread.join();
}
void Host::ping_test(uint32_t destination_ip, int number_of_pings, int delay) {
// make the threads for receiving and demuxing/sending
thread receiver_thread(&Host::receiver,this);
thread mux_demux_thread(&Host::demuxer,this);
// get an IP
DHCP_client();
usleep(delay);
// send some arps
if (!is_broadcast(destination_ip)) {
// arping(destination_ip,3,DEFAULT_TIMEOUT);
#ifdef DEBUG
host_print("Pinging");
#endif
ping(destination_ip,number_of_pings);
}
// let them run
receiver_thread.join();
mux_demux_thread.join();
}
void Host::dhcp_test() {
// make the threads for receiving and demuxing/sending
thread receiver_thread(&Host::receiver,this);
thread mux_demux_thread(&Host::demuxer,this);
// arping(destination_ip,3,DEFAULT_TIMEOUT);
#ifdef DEBUG
host_print("Getting IP");
#endif
DHCP_client();
receiver_thread.join();
mux_demux_thread.join();
}
void Host::tcp_test(const char * filename, uint32_t destination_ip, uint16_t source_port, uint16_t destination_port,
int is_client) {
// make the threads for receiving and demuxing/sending
thread receiver_thread(&Host::receiver,this);
thread mux_demux_thread(&Host::demuxer,this);
// get an IP
DHCP_client();
Socket * TCP_socket = create_socket(source_port, IP_PROTOCOL_TCP);
// send some arps
if (is_client) {
// arping(destination_ip,3,DEFAULT_TIMEOUT);
#ifdef L4_DEBUG
host_print("starting TCP client");
#endif
TCP_client(filename,source_port,destination_ip,destination_port, TCP_socket);
} else {
#ifdef L4_DEBUG
host_print("starting TCP server");
#endif
TCP_server(filename,source_port, TCP_socket);
}
delete_socket(TCP_socket->get_port());
// let them run
receiver_thread.join();
mux_demux_thread.join();
}
void Host::udp_test(const char * filename, uint32_t destination_ip, uint16_t source_port, uint16_t destination_port,
int is_client) {
// make the threads for receiving and demuxing/sending
thread receiver_thread(&Host::receiver,this);
thread mux_demux_thread(&Host::demuxer,this);
// get an IP
DHCP_client();
Socket * UDP_socket = create_socket(source_port, IP_PROTOCOL_UDP);
// send some arps
if (is_client) {
// arping(destination_ip,3,DEFAULT_TIMEOUT);
#ifdef L4_DEBUG
host_print("starting UDP client");
#endif
UDP_client(filename,source_port,destination_ip,destination_port, UDP_socket);
} else {
#ifdef L4_DEBUG
host_print("starting UDP server");
#endif
UDP_server(filename,source_port,UDP_socket);
}
delete_socket(UDP_socket->get_port());
receiver_thread.join();
mux_demux_thread.join();
}
uint32_t Host::get_ip() {return ip;}
vector<uint8_t> Host::get_mac() {return mac;}
int Host::get_frame_count() {return rx_frame_count;}
void Host::set_ip(uint32_t ip_addr) {
ip = ip_addr;
}
void Host::set_mac(vector<uint8_t> mac_addr) {
mac = mac_addr;
}
void Host::connect_ethernet(EthernetLink* e_link, bool flip_wires) {
// the reason for flipping the wires involved in this link is that if we were to call the same
// function on both sides of the link without flipping the wires, both ends would be using
// the same link for transmissions and the same link for receptions, meaning nothing would get through
if (!flip_wires) {
tx_interface = e_link->get_wire_1();
rx_interface = e_link->get_wire_2();
} else {
rx_interface = e_link->get_wire_1();
tx_interface = e_link->get_wire_2();
}
}
void Host::receiver() {
// sometimes the processing of frames is delayed, so it is better to just remove the
// frame from the link and add it to the queue so that the link is cleared and the
// frame can be processed when the host has available resources
MPDU* frame;
// this is to be run in a thread that will wait on a condition variable to check the rx
// interface.
while (1) {
frame = rx_interface->receive();
usleep((rand() % 1000) + 100);
frame_queue->add(frame);
#ifdef DEBUG
host_print(to_string(frame_queue->size()));
#endif
}
}
void Host::demuxer() {
// checks the main frame queue and moves MPDUs accordingly.
// MPDUs whose source MAC address matches the host address
// transfered out of the host and all others are processed
MPDU* frame;
vector<uint8_t> destination_mac;
while(1) {
// get the next frame
frame = frame_queue->remove();
// this frame is inbound.
// first thing's first: check to see if the frame should be inspected
// (if the frame is broadcast or specifically destined for the host)
#ifdef DEBUG
host_print("got rx frame");
#endif
destination_mac = frame->get_destination_mac();
if (compare_macs(destination_mac, mac) || is_broadcast(destination_mac)) {
#ifdef DEBUG
host_print("process frame");
#endif
process_frame(frame, destination_mac);
}
}
}
void Host::send_MPDU(MPDU* frame) {
#ifdef DEBUG
host_print("sending " +to_string(frame->get_size()) + " bytes to " +
mac_to_string(frame->get_destination_mac()));
#endif
usleep((rand() % 1000) + 100);
// and that frame is sent out on the link
tx_interface->transmit(frame);
}
void Host::process_frame(MPDU* frame, vector<uint8_t> destination_mac) {
// the contents of received frames will be processed in this function
TCP tcp_segment;
UDP udp_segment;
increment_frame_count();
// here is where something would be done with the frame (e.g. go to running ping process)
switch(frame->get_SDU_type()) {
case MPDU_ARP_TYPE:
receive_arp(frame->get_SDU());
delete frame;
break;
case MPDU_IP_TYPE: // IP packet
// use the protocol of the frame to figure out how to proceed with the contents
IP packet(frame->get_SDU());
int socket_found = 0;
switch(packet.get_protocol()) {
case IP_PROTOCOL_ICMP:
// this is a ping, so send the raw frame to the ICMP socket if it's open
for (vector<Socket*>::iterator it = open_ports.begin();
it != open_ports.end();++it) {
// check to see if this is an ICMP socket
if ((*it)->get_protocol() == IP_PROTOCOL_ICMP) {
socket_found = 1;
// give this frame to the running ping process' socket
(*it)->add_frame(frame);
}
}
if (!socket_found) {
// there's no open socket for a running ping process, so just send it
// the generic ICMP processor and delete it
process_ICMP_message(frame);
}
break;
case IP_PROTOCOL_TCP:
// this is a TCP segment, so retrieve the TCP object
// NOTE: make function to determine the port based on the contents of the SDU
tcp_segment = generate_TCP(packet.get_SDU());
// There may be several flows running
// so check to see which socket to use
for (vector<Socket*>::iterator it = open_ports.begin();
it != open_ports.end(); ++it) {
// check to see if this port represents a TCP socket and is the same
if ((*it)->get_protocol() == IP_PROTOCOL_TCP && (*it)->get_port() == tcp_segment.destination_port) {
// send the frame to this port if so
(*it)->add_frame(frame);
}
}
break;
case IP_PROTOCOL_UDP:
// this is a TCP segment, so retrieve the UDP object
// NOTE: make function to determine the port based on the contents of the SDU
udp_segment = generate_UDP(packet.get_SDU());
// There may be several flows running
// so check to see which socket to use
for (vector<Socket*>::iterator it = open_ports.begin();
it != open_ports.end(); ++it) {
// check to see if this port represents a TCP socket and is the same
if ((*it)->get_protocol() == IP_PROTOCOL_UDP && (*it)->get_port() == udp_segment.destination_port) {
// send the frame to this port if so
(*it)->add_frame(frame);
break;
}
}
break;
}
break;
}
// if (frame->get_SDU_type() == 0x0806) {
// receive_arp(frame->get_SDU());
// }
}
void Host::increment_frame_count() {
rx_frame_count += 1;
}
void Host::fill_ping_payload(ICMP* ping_to_send) {
ping_to_send->payload.clear();
ping_to_send->payload.reserve(48);
ping_to_send->payload.push_back(0xb5);
ping_to_send->payload.push_back(0x32);
ping_to_send->payload.push_back(0x05);
for (int i = 0; i < 5; i++) {
ping_to_send->payload.push_back(0x00);
}
for (int i = 0; i < 40; i++){
ping_to_send->payload.push_back(16+i);
}
}
void Host::ping(uint32_t destination_ip, int count) {
vector<uint8_t> destination_mac;
// check to see how this ping should be handled
if (in_subnet(ip,destination_ip,netmask)) {
// this means that the ip is in the same subnet, so get the mac
destination_mac = cache.get_mac(destination_ip);
// test to see if the ip has actually been entered into the cache
// and can thus be pinged without arping
if (is_broadcast(destination_mac)) {
#ifdef DEBUG
host_print("[PING] No MAC in ARP cache for IP " + ip_to_string(destination_ip) + ". ARPING");
#endif
// there was no entry, so send an arp to get the mac
send_request_arp(destination_ip, DEFAULT_TIMEOUT);
// wait until the mac address has been found and added to the queue
while (is_broadcast(cache.get_mac(destination_ip))) {
usleep(100);
}
// the mac has been obtained, so get it
destination_mac = cache.get_mac(destination_ip);
}
#ifdef DEBUG
host_print("[PING] Got MAC " + mac_to_string(destination_mac));
#endif
} else {
// it isn't in the same subnet, so just send to the router
// and let the rest of the network/internet take care of it
destination_mac = router_mac;
}
// create an ICMP echo request frame
ICMP ping_to_send;
ping_to_send.type = ICMP_ECHO_REQUEST;
ping_to_send.identifier = rand() % 65536; // this ping will always have the same ID
ping_to_send.sequence_number = 0;
// set up the IP packet, the ICMP will have to be re-encapped at each iteration due to a change
// in the sequence number and (hopfeully later) the payload and checksum
IP ip_to_send;
ip_to_send.set_destination_ip(destination_ip);
ip_to_send.set_source_ip(ip);
// create a socket that will be used to send
Socket* icmp_socket = create_socket(7, IP_PROTOCOL_ICMP);
MPDU* rx_frame;
ICMP rx_ping;
IP rx_ip;
vector<float> test_times;
test_times.reserve(count);
int transmission_attempts = 0;
int receptions = 0;
int check_count = count;
// send it as many times as requested
while (true) {
ping_to_send.sequence_number++;
fill_ping_payload(&ping_to_send);
ip_to_send.encap_SDU(ping_to_send);
#ifdef DEBUG
host_print("[PING] Sending ping " + to_string(ping_to_send.sequence_number)+ "/" + to_string(count));
#endif
// encapsulate IP in an MPDU
MPDU* ping_mpdu = new MPDU();
ping_mpdu->encap_SDU(ip_to_send);
ping_mpdu->set_source_mac(mac);
ping_mpdu->set_destination_mac(destination_mac);
chrono::time_point<chrono::high_resolution_clock> ping_send_time =
chrono::high_resolution_clock::now();
send_MPDU(ping_mpdu);
transmission_attempts++;
check_count--;
do {
// wait on the reply in the socket
rx_frame = icmp_socket->get_frame();
rx_ip = generate_IP(rx_frame->get_SDU());
rx_ping = generate_ICMP(rx_ip.get_SDU());
// figure out the type of the ICMP frame
switch(rx_ping.type) {
case ICMP_ECHO_REQUEST:
// send this raw frame to the function that processes requests
process_ICMP_message(rx_frame);
break;
case ICMP_ECHO_REPLY:
// in the future we'll have to somehow differentiate between
// multiple ping processess so we should check the id, but for now
// we should only be getting pings replies with the same id
if (rx_ping.identifier == ping_to_send.identifier) {
#ifdef DEBUG
host_print("[PING] Received ping reply for this process");
#endif
receptions++;
} else {
#ifdef DEBUG
host_print("[PING] Received ping reply for a different process " +
to_string(rx_ping.identifier) + " " + to_string(ping_to_send.identifier));
#endif
}
break;
}
} while (rx_ping.type != ICMP_ECHO_REPLY/*|| timeout*/);
// print the results for this iteration, be it a clean reception or a timeout
chrono::duration<double, milli> diff = chrono::high_resolution_clock::now()
- ping_send_time;
test_times.push_back((float)(diff.count()));
#ifdef DEBUG
host_print("[PING] " + to_string(rx_ip.get_total_length() - rx_ip.get_header_length()) +
" bytes from " + mac_to_string(rx_frame->get_source_mac()) + ": icmp_seq=" +
to_string(rx_ping.sequence_number) + " time=" + to_string(diff.count()) + " ms");
#endif
delete rx_frame;
if (check_count == 0) {
break;
}
// sleep for a second (can change this later on)
usleep(1000000);
}
delete_socket(icmp_socket->get_port());
host_print("--- " + ip_to_string(destination_ip) + " ping statistics ---");
print_ping_statistics(transmission_attempts, receptions, test_times);
}
void Host::print_ping_statistics(int transmissions, int receptions, vector<float> ping_times) {
// print all the details surrounding the pings
float total_time = 0;
float min_time = ping_times[0];
float max_time = ping_times[0];
float mdev = 0;
float avg_time;
for (int i = 0; i < ping_times.size(); i++) {
total_time += ping_times[i];
if (ping_times[i] < min_time) {
min_time = ping_times[i];
}
if (ping_times[i] > max_time) {
max_time = ping_times[i];
}
}
avg_time = total_time / ping_times.size();
for (int i = 0; i < ping_times.size(); i++) {
mdev += pow(ping_times[i] - avg_time, 2.0);
}
mdev /= ping_times.size();
mdev = sqrt(mdev);
host_print(to_string(transmissions) + " packets transmitted, " + to_string(receptions) +
" received, " + to_string((int)(1.0 - ((float)receptions)/((float)transmissions)) * 100) +
"% packet loss, time " + to_string(total_time) + " ms" );
host_print("rtt min/avg/max/mdev = " + to_string(min_time) + "/" +
to_string(avg_time) + "/" + to_string(max_time) + "/" + to_string(mdev));
}
void Host::process_ICMP_message(MPDU* rx_frame) {
IP rx_ip = generate_IP(rx_frame->get_SDU());
ICMP inbound_ping = generate_ICMP(rx_ip.get_SDU());
// check to see if this is a reply or a request as these will be handled differently
switch(inbound_ping.type) {
case ICMP_ECHO_REPLY:
// since the only reason we got to ths point was because there wasn't a running
// ping process, we can safely assume that this ping reply was received in error
#ifdef DEBUG
host_print("[PING] received ICMP reply in error");
#endif
break;
case ICMP_ECHO_REQUEST:
// a request does not need to be checked, instead a reply is generated immediately and sent
// don't waste time creating a new ping (ID is the same, payload is the same),
// just change the type
inbound_ping.type = ICMP_ECHO_REPLY;
// encap in IP
IP ping_ip;
ping_ip.set_source_ip(ip);
ping_ip.set_destination_ip(rx_ip.get_source_ip());
ping_ip.encap_SDU(inbound_ping);
// incap IP in MPDU
MPDU* ping_mpdu = new MPDU();
ping_mpdu->encap_SDU(ping_ip);
ping_mpdu->set_source_mac(mac);
ping_mpdu->set_destination_mac(rx_frame->get_source_mac());
#ifdef DEBUG
host_print("[PING] received ICMP request, sending reply");
#endif
// send it on its way
send_MPDU(ping_mpdu);
break;
}
delete rx_frame;
}
Socket* Host::create_socket(uint16_t requested_port_number = 1, uint8_t protocol_number = 0) {
uint16_t test_port_number = requested_port_number;
uint8_t port_found = 0;
// scan through the open ports to get the next available port number and assign it
if (open_ports.size() > 0) {
do
{
for (vector<Socket*>::iterator it = open_ports.begin(); it != open_ports.end(); ++it) {
if ((*it)->get_port() == test_port_number) {
// the port number has already been taken, so see if the next value works
port_found = 0;
test_port_number++;
break;
}
}
} while (port_found == 0);
}
// test_port_number now contains the first available port number, so use that in creating the socket
Socket* new_socket = new Socket(test_port_number, protocol_number);
open_ports.push_back(new_socket);
return new_socket;
}
void Host::delete_socket(uint16_t port_num) {
for (vector<Socket*>::iterator it = open_ports.begin(); it != open_ports.end();) {
if ((*it)->get_port() == port_num) {
delete *it;
it = open_ports.erase(it);
} else {
++it;
}
}
}
void Host::arping(uint32_t requested_ip, int count, int timeout) {
for (int i = 0; i < count; i++){
send_request_arp(requested_ip, timeout);
usleep(1000000);
}
}
void Host::send_request_arp(uint32_t requested_ip, int timeout) {
// create ARP request
ARP arp;
arp.opcode = ARP_REQUEST;
arp.sender_mac = mac;
arp.sender_ip = ip;
arp.target_mac = create_uniform_mac(0);
arp.target_ip = requested_ip;
#ifdef DEBUG
host_print("[ARP] Sending request to " + ip_to_string(requested_ip));
#endif
send_arp(arp,create_broadcast_mac());
}
void Host::send_arp(ARP tx_arp, vector<uint8_t> destination_mac) {
// pack it in an IP frame
MPDU* arp_mpdu = new MPDU;
arp_mpdu->set_source_mac(mac);
arp_mpdu->set_destination_mac(destination_mac);
arp_mpdu->encap_SDU(tx_arp);
// send it on its merry way
send_MPDU(arp_mpdu);
}
void Host::receive_arp(vector<uint8_t> arp_u8) {
ARP arp_rx = generate_ARP(arp_u8);
// first, we should check that the target is indeed this host
if (compare_ips(arp_rx.target_ip, ip)) {
// check to see if the sending address pair exists in the cache
// add if it does not exist, add it
if (is_broadcast(cache.get_mac(arp_rx.sender_ip))) {
cache.add_entry(arp_rx.sender_ip, arp_rx.sender_mac);
}
// check the opcode to see if this is a reply or a request
// a reply means that we should do nothing but add the result to the
// cache whereas a request means that we should both add the info on
// the sender to the cache as well as send a reply
if (arp_rx.opcode == ARP_REQUEST) {
// request
// modify opcode
arp_rx.opcode = ARP_REPLY;
// flip addresses
arp_rx.target_mac = arp_rx.sender_mac;
arp_rx.target_ip = arp_rx.sender_ip;
// set this hosts addresses
arp_rx.sender_mac = mac;
arp_rx.sender_ip = ip;
#ifdef DEBUG
host_print("[ARP] Sending reply to " + ip_to_string(arp_rx.target_ip));
#endif
// send off the reply
send_arp(arp_rx, arp_rx.target_mac);
} else {
#ifdef DEBUG
host_print("[ARP] Unicast reply from " + ip_to_string(arp_rx.sender_ip) + " ["
+ mac_to_string(arp_rx.sender_mac) + "]");
#endif
}
}
}
void Host::host_print(string statement) {
cout << setw(15) << name << ": " << statement << endl;
}
void Host::TCP_client(const char* filename, uint16_t this_port, uint32_t dest_ip, uint16_t dest_port, Socket * TCP_socket) {
// create socket to send and receive segments
MPDU * rx_frame, * tx_frame;
TCP tx_segment, rx_segment, waiting_segment;
IP rx_packet, tx_packet;
uint32_t expected_ACK;
uint32_t rx_SN, tx_SN, expected_SN;
bool connection_established = false;
int bytes_loaded = 0;
size_t queue_size;
int triple_ACK_count;
int cwnd = 1;
// determine the maximum number of bytes that can be put in the payload and still
// make it across the network
int maximum_payload_bytes = MTU - TCP_HEADER_SIZE - IP_HEADER_SIZE - MPDU_HEADER_SIZE;
/*
This is an important object for TCP, since TCP is very much concerned with reliable data transfer.
This container will house the segments that have to be transfered so that they don't need to be created
on the fly and dropped packets can be resent quickly.
*/
vector<TCP> * segments_to_send = file_to_TCP_segments(filename, this_port, dest_port, maximum_payload_bytes);
#ifdef L4_DEBUG
host_print(to_string(segments_to_send->size()) + " segments created");
#endif
// check if the destination MAC address is known and ARP if it is not
vector<uint8_t> dest_mac = cache.get_mac(dest_ip);
if (is_broadcast(dest_mac)) {
#ifdef L4_DEBUG
host_print("[TCP client] No MAC in ARP cache for IP " + ip_to_string(dest_ip) + ". ARPING");
#endif
// there was no entry, so send an arp to get the mac
send_request_arp(dest_ip, DEFAULT_TIMEOUT);
// wait until the mac address has been found and added to the queue
while (is_broadcast(cache.get_mac(dest_ip))) {
usleep(100);
}
// the mac has been obtained, so get it
dest_mac = cache.get_mac(dest_ip);
}
#ifdef L4_DEBUG
host_print("[TCP client] starting handshake");
#endif
// generate initial sync request
tx_segment.details = TCP_SYN;
tx_segment.sequence_number = TCP_INITIAL_SN;
tx_segment.ACK_number = TCP_INITIAL_SN;
tx_segment.source_port = this_port;
tx_segment.destination_port = dest_port;
while (true) {
// encap in IP
tx_packet.encap_SDU(tx_segment);
// set parameters (these details will remain the same for subsequent sends)
tx_packet.set_source_ip(ip);
tx_packet.set_destination_ip(dest_ip);
//encap in MPDU
tx_frame = new MPDU();
// set addressing
tx_frame->encap_SDU(tx_packet);
tx_frame->set_destination_mac(dest_mac);
tx_frame->set_source_mac(mac);
//send
send_MPDU(tx_frame);
// wait for reply
rx_frame = TCP_socket->get_frame();
rx_packet = generate_IP(rx_frame->get_SDU());
rx_segment = generate_TCP(rx_packet.get_SDU());
#ifdef L4_DEBUG
host_print("[TCP client] got reply");
#endif
if ((rx_segment.ACK_number == (tx_segment.sequence_number + 1)) && (rx_segment.details & TCP_SYNACK)) {
// the ACK number and SN jive and the proper flags are set, so the connection is established
// as far as the client is concerned
#ifdef L4_DEBUG
host_print("[TCP client] connection established");
#endif
// send the final ACK necessary to complete the sync on the server side
tx_segment.details = TCP_ACK; // this is the last segment in the sync
tx_segment.sequence_number = rx_segment.ACK_number;
tx_segment.ACK_number = rx_segment.sequence_number + 1;
// at this point the SN and ACK N should both be 1
tx_segment.destination_port = dest_port;
tx_segment.source_port = this_port;
// encap in IP
tx_packet.encap_SDU(tx_segment);
// encap in MPDU
tx_frame = new MPDU(); // generate a new frame
// set addressing
tx_frame->encap_SDU(tx_packet);
tx_frame->set_destination_mac(dest_mac);
tx_frame->set_source_mac(mac);
delete rx_frame; // consume the frame sent by the server
#ifdef L4_DEBUG
host_print("[TCP client] sending final sync ACK");
#endif
//send ACK
tx_interface->transmit(tx_frame);
break;
} else {
// keep resending the SYN frame until we get a SYNACK back
// this will not happen in our case, since nothing should show up at this socket unless
// the SYN was heard and replied to with a SYNACK
#ifdef L4_DEBUG
host_print("[TCP client] did not get proper SYNACK");
#endif
continue;
}
}
#ifdef L4_DEBUG
host_print("[TCP client] handshake successful");
#endif
int next_segment_index = 0;
int last_unACKd_index = 0;
triple_ACK_count = 0; // used to see if the window should be reduced
while (last_unACKd_index < segments_to_send->size()) {
if (next_segment_index < segments_to_send->size()) {
// load the segment
tx_segment = segments_to_send->at(next_segment_index++);
// encap in IP
tx_packet.encap_SDU(tx_segment);
// encap in MPDU
tx_frame = new MPDU(); // generate a new frame
tx_frame->encap_SDU(tx_packet);
// set addressing
tx_frame->set_destination_mac(dest_mac);
tx_frame->set_source_mac(mac);
#ifdef L4_DEBUG
host_print("[TCP client] sending segment, ACK with " + to_string(tx_segment.sequence_number +
tx_segment.payload.size()));
#endif
send_MPDU(tx_frame);
}
// wait for the ack and then send a frame. Note that, because of the loop, a second frame is sent out,
// which means that the cwnd will double with each new volley of segments
rx_frame = TCP_socket->get_frame();
rx_packet = generate_IP(rx_frame->get_SDU());
rx_segment = generate_TCP(rx_packet.get_SDU());
// get the next segment we expected to be ACk'd
waiting_segment = segments_to_send->at(last_unACKd_index);
expected_SN = waiting_segment.sequence_number + waiting_segment.payload.size();
// for the last transmission to have been successful, the ACK number must be what we expect to use
// as the next SN or higher
if (rx_segment.ACK_number != expected_SN) {
#ifdef L4_DEBUG
host_print("[TCP client] expected ACK " + to_string(waiting_segment.sequence_number +
waiting_segment.payload.size()) + ", got " + to_string(rx_segment.ACK_number));
#endif
if (rx_segment.ACK_number > expected_SN ) {
// this might not be the expected ACK, but it signifies that previous segments were received
// properly and so we can update up to which value fo segment was received
while (true) {
waiting_segment = segments_to_send->at(++last_unACKd_index);
if (rx_segment.ACK_number == waiting_segment.sequence_number + waiting_segment.payload.size()) {
last_unACKd_index++;
break;
}
}
} else {
// NOTE: figure out how to deal with in-flight ACKs after receiving a triple ACK.
// Scenario: have sent out 20 segments, 2nd gets lost, see three ACKs for
// first segment, so drop the cwnd and send out frames again, but there will
// still be a ton of ACKs for 2 still coming through, more than enough to drop
// cwnd down again
// check to see if the window should be reduced
// triple_ACK_count++;
// // wait for two more ACKs
// rx_frame = TCP_socket->get_frame();
// rx_packet = generate_IP(rx_frame->get_SDU());
// rx_segment = generate_TCP(rx_packet.get_SDU());
// if (triple_ACK_count == 3) {
// if (cwnd > 2) {
// cwnd /= 2;
// }
// triple_ACK_count = 0;
// // break;
// }