网络层 (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_;
// 序列化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)