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 &ethernet_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;
}
posted @ 2021-02-16 16:42  星見遥  阅读(552)  评论(0编辑  收藏  举报