-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy patharp.cpp
239 lines (214 loc) · 8.64 KB
/
arp.cpp
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
#include "arp.h"
#include "config.h"
#include "ethernet.h"
#include "ip.h"
#include "log.h"
#include "my_buf.h"
#include "net.h"
#include "utils.h"
#include <cstring>
/**
* ARPテーブル
* グローバル変数にテーブルを保持
*/
arp_table_entry arp_table[ARP_TABLE_SIZE];
/**
* ARPテーブルにエントリの追加と更新
* @param dev
* @param mac_addr
* @param ip_addr
*/
void add_arp_table_entry(net_device *dev, uint8_t *mac_addr, uint32_t ip_addr) {
// 候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの
const uint32_t index = ip_addr % ARP_TABLE_SIZE;
arp_table_entry *candidate = &arp_table[index];
// テーブルに入れられるか確認
if (candidate->ip_addr == 0 or
candidate->ip_addr == ip_addr) { // 初めの候補の場所に入れられるとき
// エントリをセット
memcpy(candidate->mac_addr, mac_addr, 6);
candidate->ip_addr = ip_addr;
candidate->dev = dev;
return;
}
// 入れられなかった場合は、その候補にあるエントリに連結する
while (candidate->next != nullptr) { // 連結リストの末尾までたどる
candidate = candidate->next;
// 途中で同じIPアドレスのエントリがあったら、そのエントリを更新する
if (candidate->ip_addr == ip_addr) {
memcpy(candidate->mac_addr, mac_addr, 6);
candidate->ip_addr = ip_addr;
candidate->dev = dev;
return;
}
}
// 連結リストの末尾に新しくエントリを作成
candidate->next = (arp_table_entry *)calloc(1, sizeof(arp_table_entry));
memcpy(candidate->next->mac_addr, mac_addr, 6);
candidate->next->ip_addr = ip_addr;
candidate->next->dev = dev;
}
/**
* ARPテーブルの検索
* @param ip_addr
*/
arp_table_entry *search_arp_table_entry(uint32_t ip_addr) {
// 初めの候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの
arp_table_entry *candidate = &arp_table[ip_addr % ARP_TABLE_SIZE];
if (candidate->ip_addr ==
ip_addr) { // 候補のエントリが検索しているIPアドレスの物だったら
return candidate;
} else if (candidate->ip_addr ==
0) { // 候補のエントリが登録されていなかったら
return nullptr;
}
// 候補のエントリが検索しているIPアドレスの物でなかった場合、そのエントリの連結リストを調べる
while (candidate->next != nullptr) {
candidate = candidate->next;
if (candidate->ip_addr ==
ip_addr) { // 連結リストの中に検索しているIPアドレスの物があったら
return candidate;
}
}
// 連結リストの中に見つからなかったら
return nullptr;
}
/**
* ARPテーブルの出力
*/
void dump_arp_table_entry() {
printf("|---IP ADDRESS----|----MAC "
"ADDRESS----|------DEVICE-------|-INDEX-|\n");
for (int i = 0; i < ARP_TABLE_SIZE; ++i) {
if (arp_table[i].ip_addr == 0) {
continue;
}
// エントリの連結リストを順に出力する
for (arp_table_entry *entry = &arp_table[i]; entry;
entry = entry->next) {
printf("| %15s | %14s | %17s | %04d |\n", ip_htoa(entry->ip_addr),
mac_addr_toa(entry->mac_addr), entry->dev->name, i);
}
}
printf("|-----------------|-------------------|-------------------|-------|"
"\n");
}
/**
* ARPリクエストの送信
* @param dev
* @param search_ip
*/
void send_arp_request(net_device *dev, uint32_t ip_addr) {
LOG_ARP("Sending arp request via %s for %s\n", dev->name, ip_htoa(ip_addr));
auto *arp_mybuf = my_buf::create(ARP_ETHERNET_PACKET_LEN);
auto *arp_msg = reinterpret_cast<arp_ip_to_ethernet *>(arp_mybuf->buffer);
arp_msg->htype = htons(ARP_HTYPE_ETHERNET); // ハードウェアタイプの設定
arp_msg->ptype = htons(ETHER_TYPE_IP); // プロトコルタイプの設定
arp_msg->hlen = ETHERNET_ADDRESS_LEN; // ハードウェアアドレス帳の設定
arp_msg->plen = IP_ADDRESS_LEN; // プロトコルアドレス長の設定
arp_msg->op =
htons(ARP_OPERATION_CODE_REQUEST); // オペレーションコードの設定
memcpy(arp_msg->sha, dev->mac_addr,
6); // 送信者ハードウェアアドレスにデバイスのMACアドレスを設定
arp_msg->spa = htonl(
dev->ip_dev
->address); // 送信者プロトコルアドレスにデバイスのIPアドレスを設定
arp_msg->tpa = htonl(
ip_addr); // ターゲットプロトコルアドレスに、探すホストのIPアドレスを設定
// イーサネットで送信する
ethernet_encapsulate_output(dev, ETHERNET_ADDRESS_BROADCAST, arp_mybuf,
ETHER_TYPE_ARP);
}
// 宣言のみ
void arp_request_arrives(net_device *dev, arp_ip_to_ethernet *request);
void arp_reply_arrives(net_device *dev, arp_ip_to_ethernet *reply);
/**
* ARPパケットの受信処理
* @param input_dev
* @param buffer
* @param len
*/
void arp_input(net_device *input_dev, uint8_t *buffer, ssize_t len) {
// ARPパケットの想定より短かったら
if (len < sizeof(arp_ip_to_ethernet)) {
LOG_ARP("Too short arp packet\n");
return;
}
auto *arp_msg = reinterpret_cast<arp_ip_to_ethernet *>(buffer);
uint16_t op = ntohs(arp_msg->op);
switch (ntohs(arp_msg->ptype)) {
case ETHER_TYPE_IP:
if (arp_msg->hlen != ETHERNET_ADDRESS_LEN) {
LOG_ARP("Illegal hardware address length\n");
return;
}
if (arp_msg->plen != IP_ADDRESS_LEN) {
LOG_ARP("Illegal protocol address length\n");
return;
}
// オペレーションコードによって分岐
if (op == ARP_OPERATION_CODE_REQUEST) {
// ARPリクエストの受信
arp_request_arrives(input_dev, arp_msg);
return;
} else if (op == ARP_OPERATION_CODE_REPLY) {
// ARPリプライの受信
arp_reply_arrives(input_dev, arp_msg);
return;
}
break;
}
}
/**
* ARPリクエストパケットの受信処理
* @param dev
* @param request
*/
void arp_request_arrives(net_device *dev, arp_ip_to_ethernet *request) {
if (dev->ip_dev != nullptr and
dev->ip_dev->address !=
IP_ADDRESS(
0, 0, 0,
0)) { // IPアドレスが設定されているデバイスからの受信だったら
if (dev->ip_dev->address ==
ntohl(request->tpa)) { // 要求されているアドレスが自分の物だったら
LOG_ARP("Sending arp reply via %s\n", ip_ntoa(request->tpa));
auto *reply_mybuf = my_buf::create(ARP_ETHERNET_PACKET_LEN);
auto reply_msg =
reinterpret_cast<arp_ip_to_ethernet *>(reply_mybuf->buffer);
reply_msg->htype = htons(ARP_HTYPE_ETHERNET);
reply_msg->ptype = htons(ETHER_TYPE_IP);
reply_msg->hlen = ETHERNET_ADDRESS_LEN; // IPアドレスの長さ
reply_msg->plen = IP_ADDRESS_LEN; // MACアドレスの長さ
reply_msg->op = htons(ARP_OPERATION_CODE_REPLY);
// 返答の情報を書き込む
memcpy(reply_msg->sha, dev->mac_addr, ETHERNET_ADDRESS_LEN);
reply_msg->spa = htonl(dev->ip_dev->address);
memcpy(reply_msg->tha, request->sha, ETHERNET_ADDRESS_LEN);
reply_msg->tpa = request->spa;
ethernet_encapsulate_output(dev, request->sha, reply_mybuf,
ETHER_TYPE_ARP); // イーサネットで送信
add_arp_table_entry(
dev, request->sha,
ntohl(request->spa)); // ARPリクエストからもエントリを生成
return;
}
}
}
/**
* ARPリプライパケットの受信処理
* @param dev
* @param reply
*/
void arp_reply_arrives(net_device *dev, arp_ip_to_ethernet *reply) {
if (dev->ip_dev != nullptr and
dev->ip_dev->address !=
IP_ADDRESS(
0, 0, 0,
0)) { // IPアドレスが設定されているデバイスからの受信だったら
LOG_ARP("Added arp table entry by arp reply (%s => %s)\n",
ip_ntoa(reply->spa), mac_addr_toa(reply->sha));
add_arp_table_entry(dev, reply->sha,
ntohl(reply->spa)); // ARPテーブルエントリの追加
}
}