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