forked from FrankQQQQ/CSC458-PA1
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sr_router.c
417 lines (369 loc) · 15.6 KB
/
sr_router.c
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
#include <stdio.h>
#include <assert.h>
#include "sr_if.h"
#include "sr_rt.h"
#include "sr_router.h"
#include "sr_protocol.h"
#include "sr_arpcache.h"
#include "sr_utils.h"
#include <stdlib.h>
#include <string.h>
/*---------------------------------------------------------------------
* Method: sr_init(void)
* Scope: Global
*
* Initialize the routing subsystem
*
*---------------------------------------------------------------------*/
void sr_init(struct sr_instance* sr)
{
/* REQUIRES */
assert(sr);
/* Initialize cache and cache cleanup thread */
sr_arpcache_init(&(sr->cache));
pthread_attr_init(&(sr->attr));
pthread_attr_setdetachstate(&(sr->attr), PTHREAD_CREATE_JOINABLE);
pthread_attr_setscope(&(sr->attr), PTHREAD_SCOPE_SYSTEM);
pthread_attr_setscope(&(sr->attr), PTHREAD_SCOPE_SYSTEM);
pthread_t thread;
pthread_create(&thread, &(sr->attr), sr_arpcache_timeout, sr);
/* Add initialization code here! */
} /* -- sr_init -- */
/*---------------------------------------------------------------------
* Method: sr_handlepacket(uint8_t* p,char* interface)
* Scope: Global
*
* This method is called each time the router receives a packet on the
* interface. The packet buffer, the packet length and the receiving
* interface are passed in as parameters. The packet is complete with
* ethernet headers.
*
* Note: Both the packet buffer and the character's memory are handled
* by sr_vns_comm.c that means do NOT delete either. Make a copy of the
* packet instead if you intend to keep it around beyond the scope of
* the method call.
*
*---------------------------------------------------------------------*/
void sr_handlepacket(struct sr_instance* sr,
uint8_t * packet/* lent */,
unsigned int len,
char* interface/* lent */)
{
/* REQUIRES */
assert(sr);
assert(packet);
assert(interface);
printf("*** -> Received packet of length %d \n",len);
/* fill in code here */
if (sizeof(sr_ethernet_hdr_t) > len) {
fprintf(stderr, "sr_handlepacket: packet length doesn't reach the min length.\n");
return;
}
sr_ethernet_hdr_t *eth_hdr = (sr_ethernet_hdr_t *)packet;
uint16_t packet_ether_type = ethertype(packet);
/* It's a IP Packet*/
if (packet_ether_type == ethertype_ip) {
/* Handle IP Packet */
if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t))
{
printf("sr_handlepacket: IP packet doesn't meet minimum length.\n");
return;
}
/* IP header is after the Ethernet header */
sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t));
printf("----------- It's an IP Packet ------------\n");
print_hdr_eth(packet);
print_hdr_ip(packet + sizeof(sr_ethernet_hdr_t));
printf("------------------------------------------\n");
/* Verify checksum */
uint16_t checksum = ip_hdr->ip_sum;
ip_hdr->ip_sum = 0;
if (checksum != cksum(ip_hdr, sizeof(sr_ip_hdr_t)))
{
printf("sr_handlepacket: IP packet has incorrect checksum.\n");
return;
}
/* Check if the IP address matches the current router's IP addresses */
struct sr_if *if_walker = sr->if_list;
struct sr_if *src_inf = sr_get_interface(sr, interface);
while (if_walker)
{
if (if_walker->ip == ip_hdr->ip_dst)
{
/* Get the struct from the name */
handle_ip(sr, ip_hdr, src_inf, packet, len);
return;
}
if_walker = if_walker->next;
}
forward_ip(sr, ip_hdr, eth_hdr, packet, len, src_inf);
}
/* It's an ARP Packet type*/
else if (packet_ether_type == ethertype_arp) {
if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_arp_hdr_t))
{
fprintf(stderr, "sr_handlepacket: ARP packet doesn't meet minimum length.\n");
return;
}
sr_arp_hdr_t *arp_hdr = (sr_arp_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t));
printf("------------ It's an ARP Packet ----------------\n");
print_hdr_eth(packet);
print_hdr_arp(packet + sizeof(sr_ethernet_hdr_t));
printf("------------------------------------------------\n");
/*
* For ARP Requests: Send an ARP reply if the target IP address is one of your router’s IP addresses.
* For ARP Replies: Cache the entry if the target IP address is one of your router’s IP addresses.
* Check if target IP is one of router's IP addresses.
* */
struct sr_if *if_walker = sr->if_list;
while (if_walker)
{
if (if_walker->ip == arp_hdr->ar_tip)
{
handle_arp(sr, arp_hdr, packet, if_walker);
return;
}
if_walker = if_walker->next;
}
printf("sr_handlepacket: target IP cannot be found.\n");
}
}/* end sr_ForwardPacket */
void handle_arp(struct sr_instance *sr, sr_arp_hdr_t *arp_hdr, uint8_t *packet, struct sr_if *inf)
{
switch (ntohs(arp_hdr->ar_op))
{
case arp_op_request:
{
printf("Received an ARP request\n");
/* Construct ARP reply */
unsigned int len = sizeof(sr_ethernet_hdr_t) + sizeof(sr_arp_hdr_t);
uint8_t *arp_reply = malloc(len);
/* Set Ethernet Header */
sr_ethernet_hdr_t *reply_eth_hdr = (sr_ethernet_hdr_t *)arp_reply;
memcpy(reply_eth_hdr->ether_dhost, ((sr_ethernet_hdr_t *)packet)->ether_shost, ETHER_ADDR_LEN);
memcpy(reply_eth_hdr->ether_shost, inf->addr, ETHER_ADDR_LEN);
reply_eth_hdr->ether_type = htons(ethertype_arp);
/* Set ARP Header */
sr_arp_hdr_t *reply_arp_hdr = (sr_arp_hdr_t *)(arp_reply + sizeof(sr_ethernet_hdr_t));
reply_arp_hdr->ar_hrd = arp_hdr->ar_hrd;
reply_arp_hdr->ar_pro = arp_hdr->ar_pro;
reply_arp_hdr->ar_hln = arp_hdr->ar_hln;
reply_arp_hdr->ar_pln = arp_hdr->ar_pln;
reply_arp_hdr->ar_op = htons(arp_op_reply);
/* set sender MAC to be interface's MAC and set sender IP to be interface's IP*/
memcpy(reply_arp_hdr->ar_sha, inf->addr, ETHER_ADDR_LEN);
reply_arp_hdr->ar_sip = inf->ip;
/* set target MAC to be the packet's sender MAC and set target IP to be the packet's sender IP*/
memcpy(reply_arp_hdr->ar_tha, arp_hdr->ar_sha, ETHER_ADDR_LEN);
reply_arp_hdr->ar_tip = arp_hdr->ar_sip;
printf("------------ ARP Reply ------------------\n");
print_hdr_eth(arp_reply);
print_hdr_arp(arp_reply + sizeof(sr_ethernet_hdr_t));
printf("-----------------------------------------\n");
sr_send_packet(sr, arp_reply, len, inf->name);
free(arp_reply);
break;
}
case arp_op_reply:
{
printf("Received an ARP reply.\n");
/* Look up request queue */
struct sr_arpreq *queued = sr_arpcache_insert(&sr->cache, arp_hdr->ar_sha, arp_hdr->ar_sip);
if (queued)
{
struct sr_packet *queued_pkts = queued->packets;
/* Send outstanding packets */
while (queued_pkts)
{
struct sr_if *inf = sr_get_interface(sr, queued_pkts->iface);
if (inf)
{
sr_ethernet_hdr_t *eth_hdr = (sr_ethernet_hdr_t *)(queued_pkts->buf);
memcpy(eth_hdr->ether_dhost, arp_hdr->ar_sha, ETHER_ADDR_LEN);
memcpy(eth_hdr->ether_shost, inf->addr, ETHER_ADDR_LEN);
sr_send_packet(sr, queued_pkts->buf, queued_pkts->len, queued_pkts->iface);
}
queued_pkts = queued_pkts->next;
}
sr_arpreq_destroy(&sr->cache, queued);
}
break;
}
}
}
void handle_ip(struct sr_instance *sr, sr_ip_hdr_t *ip_hdr, struct sr_if *inf, uint8_t *packet, unsigned int len)
{
if (ip_hdr->ip_p == ip_protocol_icmp)
{
printf("An ICMP message.\n");
if (len < sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_hdr_t))
{
printf("handle_ip: ICMP header doesn't meet minimum length.\n");
return;
}
/* ICMP header is after the IP header */
sr_icmp_hdr_t *icmp_hdr = (sr_icmp_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t));
printf("------------ ICMP HDR ------------------\n");
print_hdr_icmp(packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t));
printf("-----------------------------------------\n");
/* if it's an ICMP echo request, send echo reply */
if (icmp_hdr->icmp_type == 8)
{
/* Construct ICMP echo reply */
send_icmp_message(sr, packet, inf, 0, 0, len);
}
}
else
{
printf("A TCP/UDP message.\n");
/* Send ICMP type 3 code 3: Port Unreachable */
send_icmp_message(sr, packet, inf, 3, 3, len);
}
}
void send_icmp_message(struct sr_instance *sr, uint8_t *packet, struct sr_if *inf, uint8_t icmp_type, uint8_t icmp_code, unsigned int len)
{
uint8_t *icmp_packet;
unsigned int icmp_packet_len;
if (icmp_type == 0)
{ /* Echo Reply */
icmp_packet_len = len;
}
else
{
icmp_packet_len = sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_t3_hdr_t);
}
icmp_packet = malloc(icmp_packet_len);
memcpy(icmp_packet, packet, icmp_packet_len);
sr_ethernet_hdr_t *eth_hdr = (sr_ethernet_hdr_t *)icmp_packet;
memcpy(eth_hdr->ether_dhost, eth_hdr->ether_shost, sizeof(uint8_t) * ETHER_ADDR_LEN);
memcpy(eth_hdr->ether_shost, inf->addr, sizeof(uint8_t) * ETHER_ADDR_LEN);
eth_hdr->ether_type = htons(ethertype_ip);
sr_ip_hdr_t *ip_hdr = (sr_ip_hdr_t *)(icmp_packet + sizeof(sr_ethernet_hdr_t));
/* Choose which interface to send it out on */
if ((icmp_type == 0 && icmp_code == 0) || (icmp_type == 3 && icmp_code == 3))
{ /* If echo reply or port unreachable, it was meant for a router interface, so use the source destination */
ip_hdr->ip_src = ((sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)))->ip_dst;
}
else
{ /* Otherwise, use any ip from the router itself */
ip_hdr->ip_src = inf->ip;
}
ip_hdr->ip_dst = ((sr_ip_hdr_t *)(packet + sizeof(sr_ethernet_hdr_t)))->ip_src;
ip_hdr->ip_ttl = 64;
ip_hdr->ip_sum = 0;
ip_hdr->ip_p = ip_protocol_icmp;
if (icmp_type == 3)
ip_hdr->ip_len = htons(sizeof(sr_ip_hdr_t) + sizeof(sr_icmp_t3_hdr_t));
ip_hdr->ip_sum = cksum(ip_hdr, sizeof(sr_ip_hdr_t));
/* Modify ICMP header */
if (icmp_type == 0 && icmp_code == 0) /* Echo Reply */
{
sr_icmp_hdr_t *icmp_hdr = (sr_icmp_hdr_t *)(icmp_packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t));
icmp_hdr->icmp_type = icmp_type;
icmp_hdr->icmp_code = icmp_code;
icmp_hdr->icmp_sum = 0;
icmp_hdr->icmp_sum = cksum(icmp_hdr, len - sizeof(sr_ethernet_hdr_t) - sizeof(sr_ip_hdr_t));
}
else
{
sr_icmp_t3_hdr_t *icmp_hdr = (sr_icmp_t3_hdr_t *)(icmp_packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t));
icmp_hdr->icmp_type = icmp_type;
icmp_hdr->icmp_code = icmp_code;
icmp_hdr->icmp_sum = 0;
icmp_hdr->next_mtu = 0;
icmp_hdr->unused = 0;
/* Copy the internet header into the data */
memcpy(icmp_hdr->data, packet + sizeof(sr_ethernet_hdr_t), sizeof(sr_ip_hdr_t));
/* Copy the first 8 bytes of original datagram's data into the data */
memcpy(icmp_hdr->data + sizeof(sr_ip_hdr_t), packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t), 8);
icmp_hdr->icmp_sum = cksum(icmp_hdr, sizeof(sr_icmp_t3_hdr_t));
}
printf("----------- Send ICMP Message ------------\n");
print_hdr_eth(icmp_packet);
print_hdr_ip(icmp_packet + sizeof(sr_ethernet_hdr_t));
print_hdr_icmp(icmp_packet + sizeof(sr_ethernet_hdr_t) + sizeof(sr_ip_hdr_t));
printf("------------------------------------------\n");
forward_ip(sr, ip_hdr, eth_hdr, icmp_packet, icmp_packet_len, inf);
free(icmp_packet);
}
void forward_ip(struct sr_instance *sr, sr_ip_hdr_t *ip_hdr, sr_ethernet_hdr_t *eth_hdr, uint8_t *packet, unsigned int len, struct sr_if *src_inf)
{
/* Sanity Check: Minimum Length & Checksum*/
/* Decrement TTL by 1 */
ip_hdr->ip_ttl--;
if (ip_hdr->ip_ttl == 0)
{
/* Send ICMP Message Time Exceeded */
printf("ICMP Message Time Exceeded.\n");
send_icmp_message(sr, packet, src_inf, 11, 0, len);
return;
}
/* Recompute checksum and add back in */
ip_hdr->ip_sum = 0;
ip_hdr->ip_sum = cksum(ip_hdr, sizeof(sr_ip_hdr_t));
/* Check the routing table and compare the values to the destination IP address */
struct sr_rt *cur_node = sr->routing_table;
uint32_t matching_mask = 0;
uint32_t matching_address;
char inf[sr_IFACE_NAMELEN];
while (cur_node)
{
/* Compare the packet destination and the destination in the routing table node, record how many bits match */
printf("Checking Longest Prefix...\n");
check_longest_prefix(cur_node, ip_hdr->ip_dst, &matching_mask, &matching_address, inf);
cur_node = cur_node->next;
}
if (matching_address)
{
printf("Longest Prefix Matched!\n");
/*
* Check the ARP cache for the next-hop MAC address corresponding to the next-hop IP
* If it's there, send it
* Otherwise, send an ARP request for the next-hop IP (if one hasn’t been sent within the last second), and add the packet to the queue of packets waiting on this ARP request.
*/
struct sr_arpentry *matching_entry = sr_arpcache_lookup(&sr->cache, matching_address);
/* Update the destination and source information for this package */
if (matching_entry)
{
printf("There is a macthing entry.\n");
memcpy(eth_hdr->ether_dhost, matching_entry->mac, ETHER_ADDR_LEN);
memcpy(eth_hdr->ether_shost, sr_get_interface(sr, inf)->addr, ETHER_ADDR_LEN);
sr_send_packet(sr, packet, len, inf);
free(matching_entry);
}
else
{
/* There was no entry in the ARP cache */
printf("There was no entry in the ARP cache.\n");
struct sr_arpreq *req = sr_arpcache_queuereq(&sr->cache, matching_address, packet, len, inf);
handle_arpreq(req, sr);
}
}
else
{
/* Send ICMP Net unreachable */
printf("ICMP Net Unreachable.\n");
send_icmp_message(sr, packet, src_inf, 3, 0, len);
}
/* If we get here, then matching_address was null, then we drop the packet and send an error */
}
void check_longest_prefix(struct sr_rt *cur_node, uint32_t packet_dest, uint32_t *matching_mask, uint32_t *matching_address, char *inf)
{
/* Mask the packet's destination address to get the prefix */
int masked_dest = packet_dest & cur_node->mask.s_addr;
/* If the prefix matches the entry's destination as well, it's a match */
/* If doesn't work try: if (masked_dest == cur_node->dest.s_addr & cur_node->mask.s_addr) instead */
if (masked_dest == (cur_node->dest.s_addr & cur_node->mask.s_addr))
{
/* If this is true then we know that this match is our best match (since the number of bits compared was higher)
Save the data for comparison later */
if (cur_node->mask.s_addr > *matching_mask)
{
*matching_mask = cur_node->mask.s_addr;
*matching_address = cur_node->gw.s_addr;
strncpy(inf, cur_node->interface, sr_IFACE_NAMELEN);
}
/* If it's false then it's not our best match, just ignore it */
}
/* If the prefix doesn't match then we do nothing */
}