CS144 Lab Checkpoint 5: down the stack (the network interface)

网络层 (NetworkLayer)
  1. 发送数据报 (send_datagram):应用层调用 send_datagram 方法,传递需要发送的IP数据包和下一跳的IP地址。
  2. IP数据报:将数据报封装成一个IP数据包。
  3. 查找ARP缓存:检查ARP缓存中是否有目标IP地址的MAC地址。
  4. 找到目标MAC地址?:检查ARP缓存中是否有目标IP地址的MAC地址。
    • :构建以太网帧 (Build Ethernet Frame)。
    • :发送ARP请求 (send_arp_request)。
  5. 发送ARP请求 (send_arp_request):发送ARP请求,查询目标IP地址的MAC地址。
  6. 缓存ARP请求:缓存发送的ARP请求,以避免重复请求。
  7. 数据报入队:将待发送的IP数据报排队,等待ARP回复。
链路层 (DataLinkLayer)
  1. 构建以太网帧 (Build Ethernet Frame):构建包含IP数据包的以太网帧。
  2. 发送以太网帧 (Send Ethernet Frame):将以太网帧发送到TAP设备。
  3. 接收以太网帧 (Receive Ethernet Frame):TAP设备接收到以太网帧后,调用 recv_frame 方法。
  4. 解析以太网帧 (Parse Ethernet Frame):解析接收到的以太网帧,检查类型字段。
  5. 类型字段=IPv4?:检查以太网帧的类型字段是否为IPv4。
    • :处理IPv4数据报 (处理IPv4数据报)。
    • :检查类型字段是否为ARP。
  6. 类型字段=ARP?:检查以太网帧的类型字段是否为ARP。
    • :处理ARP消息 (处理ARP消息)。
    • :更新ARP缓存。
  7. 处理ARP消息:将有效负载解析为ARP消息。
  8. 更新ARP缓存 (Update ARP Cache):如果是ARP消息,更新ARP缓存。
  9. 发送ARP回复 (Send ARP Reply):如果是ARP请求,发送ARP回复。
  10. 处理等待发送的IP数据报:处理等待发送的IP数据报,使用新的MAC地址发送数据。
TAP设备 (TAPDevice)
  1. TAP设备:通过TAP设备发送和接收数据。
  2. 发送数据 (Send Data):通过TAP设备发送以太网帧。
  3. 接收数据 (Receive Data):通过TAP设备接收以太网帧。

附上SequenceDiagram.org流程图资源代码

network_interface.hh:

#pragma once
#include <chrono>

#include<unordered_map>
#include <queue>
#include <memory>

#include "address.hh"
#include "ethernet_frame.hh"
#include "ipv4_datagram.hh"

// 一个连接IP(互联网层或网络层)与Ethernet(网络访问层或链路层)的“网络接口”。

// 该模块是TCP/IP协议栈的最低层(将IP与较低层网络协议,例如Ethernet,连接起来)。
// 但是同一个模块也作为路由器的一部分被重复使用:
// 路由器通常具有许多网络接口,路由器的工作是在这些不同的接口之间路由Internet数据报。

// 网络接口将数据报(来自“客户”,例如TCP/IP协议栈或路由器)转换为Ethernet帧。
// 为了填写Ethernet目标地址,它会查找每个数据报的下一个IP跳的Ethernet地址,使用地址解析协议(ARP)进行请求。
// 在相反的方向上,网络接口接受Ethernet帧,检查它们是否是为其目的地的,如果是,则根据其类型处理有效载荷。
// 如果是IPv4数据报,网络接口将其传递给上层协议栈。如果是ARP请求或回复,网络接口会根据需要处理帧并学习或回复。
class NetworkInterface
{
public:
  // 一个物理输出端口的抽象,网络接口在其中发送Ethernet帧
  class OutputPort
  {
  public:
    virtual void transmit(const NetworkInterface& sender, const EthernetFrame& frame) = 0;
    virtual ~OutputPort() = default;
  };

  // 使用给定的Ethernet(网络访问层)和IP(互联网层)地址构造网络接口
  NetworkInterface(std::string_view name,
                   std::shared_ptr<OutputPort> port,
                   const EthernetAddress& ethernet_address,
                   const Address& ip_address);

  // 发送Internet数据报,封装在Ethernet帧中(如果已知Ethernet目标地址)。
  // 将使用ARP查找下一跳的Ethernet目标地址。发送通过在帧上调用“transmit”(成员变量)完成。
  void send_datagram(const InternetDatagram& dgram, const Address& next_hop);

  // 接收Ethernet帧并做出适当的响应。
  // 如果类型是IPv4,则将数据报推送到datagrams_in队列。
  // 如果类型是ARP请求,则从“发送者”字段中学习映射,并发送ARP回复。
  // 如果类型是ARP回复,则从“发送者”字段中学习映射。
  void recv_frame(const EthernetFrame& frame);

  // 当时间流逝时定期调用
  void tick(size_t ms_since_last_tick);

  // 访问器
  const std::string& name() const { return name_; }
  const OutputPort& output() const { return *port_; }
  OutputPort& output() { return *port_; }
  std::queue<InternetDatagram>& datagrams_received() { return datagrams_received_; }

private:
  // IPv4创建以太网帧
  bool create_IPv4frame(const InternetDatagram& dgram, uint32_t next_hop, EthernetFrame& frame);
 
  // ARP创建以太网帧
  bool create_ARPframe(const ARPMessage & arp_msg);

  // 发送ARP请求获取目标MAC地址
  void send_arp_request(uint32_t next_hop_ip);

  bool reply_arp_request(const ARPMessage & arp_msg);

  void prints();

  // 接口的人类可读名称
  std::string name_;

  // 物理输出端口(+一个使用它发送Ethernet帧的辅助函数“传输”)
  std::shared_ptr<OutputPort> port_;
  void transmit(const EthernetFrame& frame) const { port_->transmit(*this, frame); }

  // 接口的Ethernet(也称为硬件、网络访问层或链路层)地址
  EthernetAddress ethernet_address_;

  // 接口的IP(也称为互联网层或网络层)地址
  Address ip_address_;

  // 已接收的数据报
  std::queue<InternetDatagram> datagrams_received_ {};

  // ARP映射
  std::unordered_map<uint32_t,EthernetAddress> ipToEthernetMap;

  // ARP请求时间队列
  std::unordered_map<uint32_t,uint64_t> recent_arp_requests;

  // 等待发送的数据报
  std::unordered_map<uint32_t,InternetDatagram> waiting_datagrams_;
  
  // 当前时间
  uint64_t currentTime = 0;
};

network_interface.cc:

#include <iostream>

#include "arp_message.hh"
#include "exception.hh"
#include "network_interface.hh"

using namespace std;

//! \param[in] ethernet_address 接口的Ethernet(ARP称为“硬件”)地址
//! \param[in] ip_address 接口的IP(ARP称为“协议”)地址
NetworkInterface::NetworkInterface(string_view name,
                                   shared_ptr<OutputPort> port,
                                   const EthernetAddress& ethernet_address,
                                   const Address& ip_address)
    : name_(name),
      port_(notnull("OutputPort", move(port))),
      ethernet_address_(ethernet_address),
      ip_address_(ip_address),
      ipToEthernetMap(),
      recent_arp_requests(),
      waiting_datagrams_()
{
    cerr << "DEBUG: 网络接口具有以太网地址 " << to_string(ethernet_address) << " 和 IP 地址 " << ip_address.ip() << "\n";
}


//! \param[in] dgram 要发送的IPv4数据报
//! \param[in] next_hop 要发送到的接口的IP地址(通常是路由器或默认网关,但也可以是另一台主机,
//  如果直接连接到与目标相同的网络)
//  注意:可以使用Address::ipv4_numeric()方法将Address类型转换为uint32_t(原始的32位IP地址)。
void NetworkInterface::send_datagram(const InternetDatagram& dgram, const Address& next_hop)
{   
    uint32_t next_hop_ip = next_hop.ipv4_numeric();

    EthernetFrame frame;    
    // 若可查询到映射
    if(create_IPv4frame(dgram,next_hop_ip,frame)) {
        transmit(frame);
        return ;
    }


    // 如果未知目标以太网地址,发送ARP请求
    if (((currentTime - recent_arp_requests[next_hop_ip]) >= 5*1000) || 
        (!recent_arp_requests[next_hop_ip] && !currentTime)) {
        send_arp_request(next_hop_ip);
        recent_arp_requests[next_hop_ip] = currentTime;
    }

    // 待排队发送的的数据报
    waiting_datagrams_[next_hop_ip] = dgram;
}

//! \param[in] frame 输入的Ethernet帧
void NetworkInterface::recv_frame(const EthernetFrame& frame)
{   
    // 检查帧是否是发给我们的 或者广播
    if(frame.header.dst != ethernet_address_ && frame.header.dst != ETHERNET_BROADCAST){
            return ;
    }

   // 为IPV4数据报
    if(frame.header.type == frame.header.TYPE_IPv4){
        // 处理IPv4帧
        InternetDatagram dgram;
        if (parse(dgram, frame.payload)) {
            // 用于上层通信的数据报
            datagrams_received_.push(dgram);
        }
    }            
    // 为ARP数据报
    if(frame.header.type == frame.header.TYPE_ARP){
        // 处理ARP帧
        ARPMessage arp_msg;
        if (parse(arp_msg, frame.payload)) {
            ipToEthernetMap[arp_msg.sender_ip_address] = arp_msg.sender_ethernet_address;

            recent_arp_requests[arp_msg.sender_ip_address] = currentTime;
            // 如果是ARP请求并且目标是我们的IP地址,发送ARP回复
            if (reply_arp_request(arp_msg)) {
                return ;
            }

            // 如果是ARP回复且目标是我们的IP地址,处理排队的数据报
            if (create_ARPframe(arp_msg)) {
                return ;
            }
        }
   }
}


//! \param[in] ms_since_last_tick 自上次调用此方法以来的毫秒数
void NetworkInterface::tick(const size_t ms_since_last_tick)
{   currentTime += ms_since_last_tick;
    for (auto it = recent_arp_requests.begin(); it != recent_arp_requests.end(); ) {
        // ARP映射记录时间超过30秒删除
        if (it->second + 30*1000 <= currentTime) {
            ipToEthernetMap.erase(it->first);
            it = recent_arp_requests.erase(it);
            currentTime = 0;
        } else {
            ++it;
        }
    }
}



// IPv4创建以太网帧
bool NetworkInterface::create_IPv4frame(const InternetDatagram& dgram, uint32_t next_hop, EthernetFrame& frame)
{
    auto it = ipToEthernetMap.find(next_hop);
    if (it != ipToEthernetMap.end()) {
        frame.header.type = EthernetHeader::TYPE_IPv4;
        frame.header.dst = it->second;
        frame.header.src = ethernet_address_;

        // 序列化IPv4
        frame.payload = serialize(dgram);
        return true;
    }
    return false; // 返回 false 表示没有找到对应的以太网地址
}

// 利用ARP创建以太网帧
bool NetworkInterface::create_ARPframe(const ARPMessage & arp_msg){
    if(arp_msg.opcode == ARPMessage::OPCODE_REPLY 
                && arp_msg.target_ip_address == ip_address_.ipv4_numeric()){

        EthernetFrame frame_;
        auto it = waiting_datagrams_.find(arp_msg.sender_ip_address);       
        if (it != waiting_datagrams_.end()) {
            frame_.header.type = EthernetHeader::TYPE_IPv4;
            frame_.header.src = ethernet_address_;
            frame_.header.dst = arp_msg.sender_ethernet_address;
            frame_.payload = serialize(it->second);
            transmit(frame_);   
            waiting_datagrams_.erase(it); 
        }
        return true;
    }else{
        return false;
    }
}

// 发送ARP请求获取目标MAC地址
void NetworkInterface::send_arp_request(uint32_t next_hop_ip) {
    // 构建ARP请求
    ARPMessage arp_request;
    arp_request.opcode = ARPMessage::OPCODE_REQUEST;            // 操作码:请求
    
    arp_request.sender_ethernet_address = ethernet_address_;    // 发送方MAC地址
    arp_request.sender_ip_address = ip_address_.ipv4_numeric(); // 发送方IP地址
    
    arp_request.target_ethernet_address = EthernetAddress{0, 0, 0, 0, 0, 0}; // 目标MAC地址:全零
    arp_request.target_ip_address = next_hop_ip; // 目标IP地址

    // ARP的数据帧
    EthernetFrame frame;
    frame.header.src = ethernet_address_;
    frame.header.dst = ETHERNET_BROADCAST;
    frame.header.type = EthernetHeader::TYPE_ARP;
    
    // 序列化ARP请求
    Serializer serializer;
    arp_request.serialize(serializer);
    frame.payload = serializer.output();

    transmit(frame);
}

// 回复ARP请求
bool NetworkInterface::reply_arp_request(const ARPMessage & arp_msg){
    if(arp_msg.opcode == ARPMessage::OPCODE_REQUEST 
        && arp_msg.target_ip_address == ip_address_.ipv4_numeric()){
        // 构建reply ARP
        ARPMessage arp_reply;
        arp_reply.opcode = ARPMessage::OPCODE_REPLY;
        arp_reply.sender_ethernet_address = ethernet_address_;
        arp_reply.sender_ip_address = ip_address_.ipv4_numeric();
        arp_reply.target_ethernet_address = arp_msg.sender_ethernet_address;
        arp_reply.target_ip_address = arp_msg.sender_ip_address;

        EthernetFrame reply_frame;
        reply_frame.header.type = EthernetHeader::TYPE_ARP;
        reply_frame.header.src = ethernet_address_;
        reply_frame.header.dst = arp_msg.sender_ethernet_address;
        reply_frame.payload = serialize(arp_reply);  
        transmit(reply_frame);        
        return true;   
    }else{
        return false;
    }
}


void NetworkInterface::prints(){
    cout<<"当前所有映射 "<<endl;
    for(auto it = ipToEthernetMap.begin();it != ipToEthernetMap.end();it++){
        cout<<"IP序列:  "<<it->first<<endl;
        cout<<"MAC地址: "<<to_string(it->second)<<endl;
        cout<<"对应时间"<<recent_arp_requests[it->first]<<endl;
        cout<<"当前时间"<<currentTime<<endl;
    }
}

已经更新仓库lms2004/minnow: CS 144 networking lab (github.com)

相关推荐

  1. CS144 Lab Checkpoint 5: down the stack (the network interface)

    2024-05-25 23:46:25       30 阅读
  2. CSS-5

    2024-05-25 23:46:25       55 阅读
  3. IEC60870-5-104规约

    2024-05-25 23:46:25       49 阅读
  4. 14.5 同步

    2024-05-25 23:46:25       62 阅读
  5. 5.14学习总结

    2024-05-25 23:46:25       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-25 23:46:25       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-25 23:46:25       101 阅读
  3. 在Django里面运行非项目文件

    2024-05-25 23:46:25       82 阅读
  4. Python语言-面向对象

    2024-05-25 23:46:25       91 阅读

热门阅读

  1. vue富文本层级高

    2024-05-25 23:46:25       34 阅读
  2. 信息系统管理工程师问答题

    2024-05-25 23:46:25       30 阅读
  3. 量子计算在科技浪潮中的引领作用

    2024-05-25 23:46:25       33 阅读
  4. LeetCode399触发求值

    2024-05-25 23:46:25       34 阅读
  5. MySQL和MongoDB数据库的区别

    2024-05-25 23:46:25       32 阅读
  6. Python——字典数据存入excel

    2024-05-25 23:46:25       36 阅读