-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharpcache.c
209 lines (193 loc) · 7.1 KB
/
arpcache.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
#include "arpcache.h"
#include "arp.h"
#include "ether.h"
#include "packet.h"
#include "icmp.h"
#include "ip.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
static arpcache_t arpcache;
// initialize IP->mac mapping, request list, lock and sweeping thread
void arpcache_init()
{
bzero(&arpcache, sizeof(arpcache_t));
init_list_head(&(arpcache.req_list));
pthread_mutex_init(&arpcache.lock, NULL);
pthread_create(&arpcache.thread, NULL, arpcache_sweep, NULL);
}
// release all the resources when exiting
void arpcache_destroy()
{
pthread_mutex_lock(&arpcache.lock);
struct arp_req *req_entry = NULL, *req_q;
list_for_each_entry_safe(req_entry, req_q, &(arpcache.req_list), list) {
struct cached_pkt *pkt_entry = NULL, *pkt_q;
list_for_each_entry_safe(pkt_entry, pkt_q, &(req_entry->cached_packets), list) {
list_delete_entry(&(pkt_entry->list));
free(pkt_entry->packet);
free(pkt_entry);
}
list_delete_entry(&(req_entry->list));
free(req_entry);
}
pthread_kill(arpcache.thread, SIGTERM);
pthread_mutex_unlock(&arpcache.lock);
}
// lookup the IP->mac mapping
//
// traverse the hash table to find whether there is an entry with the same IP
// and mac address with the given arguments
int arpcache_lookup(u32 ip4, u8 *mac)
{
fprintf(stderr, "TODO: lookup ip address in arp cache.\n");
int i,j;
pthread_mutex_lock(&arpcache.lock);
fprintf(stderr, IP_FMT,LE_IP_FMT_STR(ip4));
for(i = 0;i < MAX_ARP_SIZE;i++){
if(arpcache.entries[i].valid == 1 && arpcache.entries[i].ip4 == ip4){
//fprintf(stderr, "TODO: I find address in arp cache.\n");
fprintf(stderr, "%d\n",i);
//memcpy(mac,arpcache.entries[i].mac,ETH_ALEN);
for(j =0; j < ETH_ALEN;j++) mac[j] = arpcache.entries[i].mac[j];
pthread_mutex_unlock(&arpcache.lock);
return 1;
}
else
continue;
}
pthread_mutex_unlock(&arpcache.lock);
return 0;
}
// append the packet to arpcache
//
// Lookup in the hash table which stores pending packets, if there is already an
// entry with the same IP address and iface (which means the corresponding arp
// request has been sent out), just append this packet at the tail of that entry
// (the entry may contain more than one packet); otherwise, malloc a new entry
// with the given IP address and iface, append the packet, and send arp request.
void arpcache_append_packet(iface_info_t *iface, u32 ip4, char *packet, int len)
{
fprintf(stderr, "TODO: append the ip address if lookup failed, and send arp request if necessary.\n");
pthread_mutex_lock(&arpcache.lock);
struct arp_req *arp_entry = NULL;
struct cached_pkt *pkt_ptr = NULL;
int flag = 0;
list_for_each_entry(arp_entry,&arpcache.req_list,list){
if(arp_entry->ip4 == ip4 && arp_entry->iface == iface){
flag = 1;
break;
}
}
pkt_ptr = malloc(sizeof(struct cached_pkt));
init_list_head(&(pkt_ptr->list));
pkt_ptr->packet = packet;
pkt_ptr->len = len;
if(flag == 0){
arp_entry = malloc(sizeof(struct arp_req));
arp_entry->iface = iface;
arp_entry->ip4 = ip4;
arp_entry->retries = 1;
arp_entry->sent = time(NULL);
init_list_head(&(arp_entry->cached_packets));
init_list_head(&(arp_entry->list));
list_add_tail(&arp_entry->list, &arpcache.req_list);
arp_send_request(iface,ip4);
}
list_add_tail(&pkt_ptr->list, &arp_entry->cached_packets);
pthread_mutex_unlock(&arpcache.lock);
}
// insert the IP->mac mapping into arpcache, if there are pending packets
// waiting for this mapping, fill the ethernet header for each of them, and send
// them out
void arpcache_insert(u32 ip4, u8 mac[ETH_ALEN])
{
fprintf(stderr, "TODO: insert ip->mac entry, and send all the pending packets.\n");
pthread_mutex_lock(&arpcache.lock);
struct cached_pkt *pkt_ptr = NULL,*pkt_nptr;
int i ;
struct arp_req *arp_entry = NULL,*arp_nentry;
struct ether_header *eh;
for(i = 0;i < MAX_ARP_SIZE;i++){
if(arpcache.entries[i].valid == 0){
arpcache.entries[i].ip4 = ip4;
fprintf(stderr, IP_FMT,LE_IP_FMT_STR(ip4));
memcpy(arpcache.entries[i].mac,mac,ETH_ALEN);
arpcache.entries[i].valid = 1;
arpcache.entries[i].added = time(NULL);
break;
}
}
list_for_each_entry_safe(arp_entry,arp_nentry,&arpcache.req_list,list){
if(arp_entry->ip4 == ip4){
list_for_each_entry_safe(pkt_ptr,pkt_nptr,&arp_entry->cached_packets,list){
eh = (struct ether_header *)(pkt_ptr->packet);
memcpy(eh->ether_dhost, mac, ETH_ALEN);
iface_send_packet(arp_entry->iface, pkt_ptr->packet, pkt_ptr->len);
list_delete_entry(&(pkt_ptr->list));
fprintf(stderr, "TODO: 1.\n");
//free(pkt_ptr->packet);
fprintf(stderr, "TODO: 2.\n");
free(pkt_ptr);
}
fprintf(stderr, "TODO: 2.\n");
list_delete_entry(&(arp_entry->list));
fprintf(stderr, "TODO: 3.\n");
free(arp_entry);
break;
}
}
fprintf(stderr, "TODO: 4.\n");
pthread_mutex_unlock(&arpcache.lock);
}
// sweep arpcache periodically
//
// For the IP->mac entry, if the entry has been in the table for more than 15
// seconds, remove it from the table.
// For the pending packets, if the arp request is sent out 1 second ago, while
// the reply has not been received, retransmit the arp request. If the arp
// request has been sent 5 times without receiving arp reply, for each
// pending packet, send icmp packet (DEST_HOST_UNREACHABLE), and drop these
// packets.
void *arpcache_sweep(void *arg)
{
while (1) {
sleep(1);
fprintf(stderr, "TODO: sweep arpcache periodically: remove old entries, resend arp requests .\n");
int i;
struct arp_req *arp_entry = NULL, *arp_nentry;
pthread_mutex_lock(&arpcache.lock);
time_t sec = time(NULL);
for(i = 0;i < MAX_ARP_SIZE;i++){
if(arpcache.entries[i].valid == 1 && (sec - arpcache.entries[i].added) > 15){
arpcache.entries[i].valid = 0;
fprintf(stderr,"old ip: %d\n",arpcache.entries[i].ip4);
}
}
list_for_each_entry_safe(arp_entry,arp_nentry,&arpcache.req_list,list){
if(sec - arp_entry->sent >= 1){
if(arp_entry->retries == 5){
struct cached_pkt *pkt_ptr = NULL,*pkt_nptr;
list_for_each_entry_safe(pkt_ptr,pkt_nptr,&arp_entry->cached_packets,list){
pthread_mutex_unlock(&arpcache.lock);
icmp_send_packet(pkt_ptr->packet,pkt_ptr->len,ICMP_DEST_UNREACH,1);
pthread_mutex_lock(&arpcache.lock);
list_delete_entry(&(pkt_ptr->list));
//free(pkt_ptr->packet);
free(pkt_ptr);
}
list_delete_entry(&(arp_entry->list));
free(arp_entry);
}
else{
arp_send_request(arp_entry->iface,arp_entry->ip4);
arp_entry->retries++;
}
}
}
pthread_mutex_unlock(&arpcache.lock);
}
return NULL;
}