CS144学习(3)ARP协议
第五个实验主要的内容是ARP地址解析协议。ARP协议位于IP层的底部,用于将下一跳的IP地址转换成MAC地址传给数据链路层。在主机中一般会有一个ARP缓存表,用于缓存最近的IP和MAC地址对应信息。ARP数据包是封装在以太网帧当中发送。
ARP请求的源IP地址和源MAC地址设置为本机的IP和MAC地址,目的IP地址设置为需要解析的IP地址,目的MAC地址设置为广播地址(以太网帧的目的MAC地址设置为广播地址,ARP报文中的目的MAC地址在规范中并没有具体要求),操作类型设置成REQUEST就行了。
ARP响应的源IP和MAC地址设置为本机的IP和MAC地址,目的IP和MAC地址设置为请求当中的源IP和MAC地址,操作类型设置为REPLY就行了。
当收到一个ARP帧后,数据链路层会将其交给上层的ARP处理程序,处理程序的具体流程如下,引用自ieft:
?Do I have the hardware type in ar$hrd?
Yes: (almost definitely)
[optionally check the hardware length ar$hln]
?Do I speak the protocol in ar$pro?
Yes:
[optionally check the protocol length ar$pln]
Merge_flag := false
If the pair <protocol type, sender protocol address> is
already in my translation table, update the sender
hardware address field of the entry with the new
information in the packet and set Merge_flag to true.
?Am I the target protocol address?
Yes:
If Merge_flag is false, add the triplet <protocol type,
sender protocol address, sender hardware address> to
the translation table.
?Is the opcode ares_op$REQUEST? (NOW look at the opcode!!)
Yes:
Swap hardware and protocol fields, putting the local
hardware and protocol addresses in the sender fields.
Set the ar$op field to ares_op$REPLY
Send the packet to the (new) target hardware address on
the same hardware on which the request was received.
也就是说,当收到一个ARP广播报文时,如果其中的信息已经缓存,就会对其进行更新,不论目标是否为自己;而只有当报文的目标为自己时,才会将ARP信息新增到缓存中去。
按照上面的流程就可以完成ARP报文的处理了:
#include "network_interface.hh"
#include "arp_message.hh"
#include "ethernet_frame.hh"
#include <iostream>
using namespace std;
NetworkInterface::NetworkInterface(const EthernetAddress ðernet_address, const Address &ip_address)
: _ethernet_address(ethernet_address), _ip_address(ip_address) {
cerr << "DEBUG: Network interface has Ethernet address " << to_string(_ethernet_address) << " and IP address "
<< ip_address.ip() << "\n";
}
void NetworkInterface::send_datagram(const InternetDatagram &dgram, const Address &next_hop) {
// convert IP address of next hop to raw 32-bit representation (used in ARP header)
const uint32_t next_hop_ip = next_hop.ipv4_numeric();
EthernetFrame frame;
frame.payload() = dgram.serialize();
frame.header().type = EthernetHeader::TYPE_IPv4;
frame.header().src = _ethernet_address;
bool found = false;
if (_arp_table.count(next_hop_ip)) {
auto record = _arp_table[next_hop_ip];
if (_ticks < record._expire_time) {
frame.header().dst = record.address;
found = true;
} else {
_arp_table.erase(next_hop_ip);
}
}
if (found) {
_frames_out.push(frame);
} else {
_waiting_frames[next_hop_ip].push_back(frame);
// send arp request
if (_last_arp.count(next_hop_ip) == 0 || _last_arp[next_hop_ip] + 5000 <= _ticks) {
ARPMessage arp_req;
arp_req.sender_ip_address = _ip_address.ipv4_numeric();
arp_req.sender_ethernet_address = _ethernet_address;
arp_req.target_ip_address = next_hop_ip;
arp_req.opcode = ARPMessage::OPCODE_REQUEST;
EthernetFrame arp_frame;
arp_frame.payload() = arp_req.serialize();
arp_frame.header().type = EthernetHeader::TYPE_ARP;
arp_frame.header().src = _ethernet_address;
arp_frame.header().dst = ETHERNET_BROADCAST;
_frames_out.push(arp_frame);
_last_arp[next_hop_ip] = _ticks;
}
}
}
optional<InternetDatagram> NetworkInterface::recv_frame(const EthernetFrame &frame) {
if (frame.header().dst != _ethernet_address && frame.header().dst != ETHERNET_BROADCAST) {
return nullopt;
}
if (frame.header().type == EthernetHeader::TYPE_IPv4) {
InternetDatagram dgram;
if (dgram.parse(frame.payload()) == ParseResult::NoError) {
return dgram;
} else {
return nullopt;
}
} else if (frame.header().type == EthernetHeader::TYPE_ARP) {
// https://tools.ietf.org/html/rfc826 Packet Reception
ARPMessage arp;
// ?Do I have the hardware type in ar$hrd?
if (arp.parse(frame.payload()) == ParseResult::NoError && arp.hardware_type == ARPMessage::TYPE_ETHERNET) {
// ?Do I speak the protocol in ar$pro?
if (arp.protocol_type == EthernetHeader::TYPE_IPv4) {
bool merge_flag = false;
// ?If the pair <protocol type, sender protocol address> is already in my translation table, update
if (_arp_table.count(arp.sender_ip_address)) {
_arp_table[arp.sender_ip_address] = {arp.sender_ethernet_address, _ticks + 30 * 1000};
merge_flag = true;
}
// ?Am I the target protocol address?
if (arp.target_ip_address == _ip_address.ipv4_numeric()) {
// ?If Merge_flag is false, add to the translation table.
if (!merge_flag) {
_arp_table[arp.sender_ip_address] = {arp.sender_ethernet_address, _ticks + 30 * 1000};
// send all waiting frames
for (auto waiting_frame : _waiting_frames[arp.sender_ip_address]) {
waiting_frame.header().dst = arp.sender_ethernet_address;
_frames_out.push(waiting_frame);
}
_waiting_frames.erase(arp.sender_ip_address);
}
// ?Is the opcode ares_op$REQUEST?
if (arp.opcode == ARPMessage::OPCODE_REQUEST) {
// send arp reply
ARPMessage arp_reply;
arp_reply.sender_ip_address = _ip_address.ipv4_numeric();
arp_reply.sender_ethernet_address = _ethernet_address;
arp_reply.target_ip_address = arp.sender_ip_address;
arp_reply.target_ethernet_address = arp.sender_ethernet_address;
arp_reply.opcode = ARPMessage::OPCODE_REPLY;
EthernetFrame reply_frame;
reply_frame.payload() = arp_reply.serialize();
reply_frame.header().type = EthernetHeader::TYPE_ARP;
reply_frame.header().src = _ethernet_address;
reply_frame.header().dst = arp.sender_ethernet_address;
_frames_out.push(reply_frame);
}
}
}
}
return nullopt;
} else {
cerr << "unknown ethernet packet type" << endl;
return nullopt;
}
}
void NetworkInterface::tick(const size_t ms_since_last_tick) {
_ticks += ms_since_last_tick;
}