The fifth experiment is mainly about ARP address resolution protocol. ARP, located at the bottom of the IP layer, translates the IP address of the next hop into a MAC address and sends the IP address to the data link layer. Hosts generally have an ARP cache table to cache the latest IP and MAC addresses. ARP packets are sent encapsulated in Ethernet frames.
The source IP and MAC addresses of ARP requests are set to the LOCAL IP and MAC addresses, the destination IP address is set to the IP address to be resolved, and the destination MAC address is set to the broadcast address. (The destination MAC address of an Ethernet frame is set to the broadcast address. The destination MAC address of AN ARP packet is not specified in the specification.) Set the action type to REQUEST.
Set the source IP address and MAC address of the ARP response to the local IP address and MAC address, the destination IP address and MAC address to the source IP address and MAC address of the request, and the operation type to REPLY.
After receiving an ARP frame, the data link layer sends it to an UPPER-layer ARP processor. The process of the ARP processor is as follows, as quoted in 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.Copy the code
That is, if the information in an ARP broadcast packet is cached, the DEVICE updates it, regardless of whether the target is itself. ARP information is added to the cache only when the packet is destined for itself.
ARP packets are processed by following the preceding procedure:
#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(a); EthernetFrame frame; frame.payload() = dgram.serialize(a); 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(a); 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(a); 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 {
returnnullopt; }}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
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(a); 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(a); 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;
returnnullopt; }}void NetworkInterface::tick(const size_t ms_since_last_tick) {
_ticks += ms_since_last_tick;
}
Copy the code