【斯坦福CS144】Lab4

news2024/11/19 4:33:29

一、实验目的

完成一个网络接口实现。

二、实验内容

完成一个网络接口实现,其大部分工作是:为每个下一跳IP地址查找(和缓存)以太网地址。而这种协议被称为地址解析协议ARP。

三、实验过程

在minnow目录下输入git merge origin/check4-startercode获取Lab4

用文本编辑器打开./src/network_interface.hh

修改代码

用文本编辑器打开./src/network_interface.cc

修改代码

在build目录下输入make进行编译

输入make check4进行测试

测试成功,实验结束。

四、实验体会

1.在实现整个网络接口时,必须确保几点:

①ARP条目 TTL 为30s,时间到期后需要将其从 ARP Table 中删除。

若发送 IP 报文时,发现 ARP Table 中无目标 MAC 地址,则立即发送 ARP 请求报文,同时将当前 IP 报文暂时缓存,直至获取到目标 MAC 地址后再重新发送。

②不同目标 IP 的 ARP 请求报文之间的发送间隔,不能超过 5s。

③如果 ARP 请求报文在 5 秒内仍然无响应,则重新发送。

④当网络接口接收到一个以太网帧时,

必须丢弃目的 MAC 地址不为当前网络接口 MAC 地址

除了 ARP 协议需要比较自己的 IP 地址以外,不要在其他任何地方进行 IP 比较,因为网络接口位于链路层。

如果是发给自己的 ARP 请求,那么要忽略掉发送来的 ARPMessage::target_ethernet_address,因为发送者自己也不知道这个要填写什么,该字段无意义。

无论接收到的是 ARP 请求包或者 ARP 响应包,只要是明确发给自己的,那么这里面的 src_ip_addr 和 src_eth_addr 都可用于更新当前的 ARP 表。

2.如何理解NetworkInterface:

①一个将IP(互联网层或网络层)与以太网(网络访问层或链路层)连接的"网络接口",该模块是TCP/IP协议栈的最底层(连接IP与更底层的网络协议,如以太网),但同样的模块也作为路由器的一部分反复使用;

②路由器通常有许多网络接口,其工作是在不同的接口之间路由互联网数据报,网络接口将来自"客户端"(例如TCP/IP协议栈或路由器)的数据报转换为以太网帧。为了填写以太网的目标地址,它查找每个数据报的下一个IP跳的以太网地址,并使用地址解析协议ARP进行请求。在相反的方向,网络接口接受以太网帧,检查它们是否是针对它的,如果是,则根据其类型处理有效载荷。

如果是IPv4数据报,网络接口将其向上传递到协议栈。

如果是ARP请求或回复,网络接口将处理该帧,并根据需要进行学习或回复。

五、代码附录

network_interface.hh

#pragma once

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

#include <iostream>
#include <list>
#include <optional>
#include <queue>
#include <unordered_map>
#include <utility>

// A "network interface" that connects IP (the internet layer, or network layer)
// with Ethernet (the network access layer, or link layer).

// This module is the lowest layer of a TCP/IP stack
// (connecting IP with the lower-layer network protocol,
// e.g. Ethernet). But the same module is also used repeatedly
// as part of a router: a router generally has many network
// interfaces, and the router's job is to route Internet datagrams
// between the different interfaces.

// The network interface translates datagrams (coming from the
// "customer," e.g. a TCP/IP stack or router) into Ethernet
// frames. To fill in the Ethernet destination address, it looks up
// the Ethernet address of the next IP hop of each datagram, making
// requests with the [Address Resolution Protocol](\ref rfc::rfc826).
// In the opposite direction, the network interface accepts Ethernet
// frames, checks if they are intended for it, and if so, processes
// the the payload depending on its type. If it's an IPv4 datagram,
// the network interface passes it up the stack. If it's an ARP
// request or reply, the network interface processes the frame
// and learns or replies as necessary.
class NetworkInterface
{
private:
  // Ethernet (known as hardware, network-access, or link-layer) address of the interface
  EthernetAddress ethernet_address_;

  // IP (known as Internet-layer or network-layer) address of the interface
  Address ip_address_;

  std::queue<EthernetFrame> send_frame_{};

  std::unordered_map<uint32_t, std::queue<InternetDatagram>> store_dgram_{};
  //std::queue<std::pair<InternetDatagram, Address>> store_dgram_{};
  //这里key值不能选Address,它没有拷贝构造函数,=重载
  std::unordered_map<uint32_t, std::pair<EthernetAddress, size_t>> map_ip2mac_{};
  std::unordered_map<uint32_t,size_t> map_arp_time_{};


public:
  // Construct a network interface with given Ethernet (network-access-layer) and IP (internet-layer)
  // addresses
  NetworkInterface( const EthernetAddress& ethernet_address, const Address& ip_address );

  // Access queue of Ethernet frames awaiting transmission
  std::optional<EthernetFrame> maybe_send();

  // Sends an IPv4 datagram, encapsulated in an Ethernet frame (if it knows the Ethernet destination
  // address). Will need to use [ARP](\ref rfc::rfc826) to look up the Ethernet destination address
  // for the next hop.
  // ("Sending" is accomplished by making sure maybe_send() will release the frame when next called,
  // but please consider the frame sent as soon as it is generated.)
  void send_datagram( const InternetDatagram& dgram, const Address& next_hop );

  // Receives an Ethernet frame and responds appropriately.
  // If type is IPv4, returns the datagram.
  // If type is ARP request, learn a mapping from the "sender" fields, and send an ARP reply.
  // If type is ARP reply, learn a mapping from the "sender" fields.
  std::optional<InternetDatagram> recv_frame( const EthernetFrame& frame );

  // Called periodically when time elapses
  void tick( size_t ms_since_last_tick );
};

network_interface.cc

#include "network_interface.hh"

#include "arp_message.hh"
#include "ethernet_frame.hh"

using namespace std;

// ethernet_address: Ethernet (what ARP calls "hardware") address of the interface
// ip_address: IP (what ARP calls "protocol") address of the interface
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";
}

// dgram: the IPv4 datagram to be sent
// next_hop: the IP address of the interface to send it to (typically a router or default gateway, but
// may also be another host if directly connected to the same network as the destination)

// Note: the Address type can be converted to a uint32_t (raw 32-bit IP address) by using the
// Address::ipv4_numeric() method.
void NetworkInterface::send_datagram( const InternetDatagram& dgram, const Address& next_hop )
{
  //以太网帧可以携带Internetdatagram,也可以携带arpmessage
  //(void)dgram;
  //(void)next_hop;
  uint32_t target_ip = next_hop.ipv4_numeric();
  //uint32_t source_address = dgram.header.src;
  //uint32_t destination_address = dgram.header.dst;
  //发送到next_hop, 而不是dgram的dst
  auto it = map_ip2mac_.find(target_ip);

  //nexthop的以太网地址已知,封装dgram发送
  if(it!=map_ip2mac_.end())
  {
    EthernetFrame send_frame
    {{it->second.first, ethernet_address_, EthernetHeader::TYPE_IPv4},serialize(dgram)};
    //send_frame.header.dst = it->second.first;
    send_frame_.push(send_frame);
    return;
  }

  //nexthop的以太网地址未知,如果没发过arp就发request arp,把dgram缓存起来
  if(map_arp_time_.find(target_ip)==map_arp_time_.end())
  {
    ARPMessage arp_send;
    arp_send.opcode = ARPMessage::OPCODE_REQUEST;
    arp_send.sender_ethernet_address = ethernet_address_;
    arp_send.sender_ip_address = ip_address_.ipv4_numeric();
    arp_send.target_ip_address = target_ip;
    EthernetFrame send_frame{{ETHERNET_BROADCAST, ethernet_address_, EthernetHeader::TYPE_ARP}, serialize(arp_send)};
    send_frame_.push(send_frame);
    map_arp_time_.insert(make_pair(target_ip, 0));//发完arp之后加入到map里面记录下来
  }
  //缓存dgram
  auto it_store_dgram = store_dgram_.find(target_ip);
  if(it_store_dgram == store_dgram_.end())
  {
    std::queue<InternetDatagram> q_dgram;
    store_dgram_.insert(make_pair(target_ip, q_dgram));
    //store_dgram_[target_ip].push(dgram);
  }
  store_dgram_[target_ip].push(dgram);
  return;
}

// frame: the incoming Ethernet frame
optional<InternetDatagram> NetworkInterface::recv_frame( const EthernetFrame& frame )
{
  if(frame.header.dst!=ethernet_address_ && frame.header.dst!=ETHERNET_BROADCAST)
  return{};//有点疑惑为什么会出现这种发错的情况,但是测试样例里面有一个这样的例子
  //(void)frame;
  //收到arp,两种情况,收到的是request或者reply
  if(frame.header.type==EthernetHeader::TYPE_ARP)
  {
    ARPMessage rec_arp;
    parse(rec_arp, frame.payload);
    map_ip2mac_.insert(make_pair(rec_arp.sender_ip_address, make_pair(rec_arp.sender_ethernet_address, 0)));
    //收到的是request,如果request的是自己的以太网地址,就发送一个reply的arpmessage,发送给request方
    if(rec_arp.opcode==ARPMessage::OPCODE_REQUEST)
    {
      if(rec_arp.target_ip_address == ip_address_.ipv4_numeric())
      {
        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 = rec_arp.sender_ethernet_address;
        arp_reply.target_ip_address = rec_arp.sender_ip_address;

        EthernetFrame reply_frame
        {{rec_arp.sender_ethernet_address, ethernet_address_, EthernetHeader::TYPE_ARP},serialize(arp_reply)};
        send_frame_.push(reply_frame);
        return{};
      }
      else return{};
    }

    //收到arp之后,无论arp是reply还是request都检查一下有没有可以发送的dgram
    auto it = store_dgram_.find(rec_arp.sender_ip_address);
    if(it!=store_dgram_.end())
    {
      while(it->second.empty()==false)
      {
        InternetDatagram dgram = it->second.front();
        it->second.pop();
        EthernetFrame send_frame{{rec_arp.sender_ethernet_address, ethernet_address_,EthernetHeader::TYPE_IPv4},serialize(dgram)};
        send_frame_.push(send_frame);
      }
      if(it->second.empty()) store_dgram_.erase(it);
    }
  }

  //frame携带的是internetdatagram
  else if(frame.header.type==EthernetHeader::TYPE_IPv4)
  {
    InternetDatagram dgram;
    parse(dgram, frame.payload);
    return dgram;
  }
  return {};
}

// ms_since_last_tick: the number of milliseconds since the last call to this method
void NetworkInterface::tick( const size_t ms_since_last_tick )
{
  for(auto it = map_ip2mac_.begin();it!=map_ip2mac_.end();)
  {
    it->second.second += ms_since_last_tick;
    if(it->second.second >= 30000)it=map_ip2mac_.erase(it);
    else it++;
  }
  
  for(auto it = map_arp_time_.begin(); it!=map_arp_time_.end();)
  {
    it->second += ms_since_last_tick;
    if(it->second>=5000) it=map_arp_time_.erase(it);
    else it++;
  }

  //for(auto it = map)
  //(void)ms_since_last_tick;
}

optional<EthernetFrame> NetworkInterface::maybe_send()
{
  if(!send_frame_.empty())
  {
    auto frame = send_frame_.front();
    send_frame_.pop();
    return {frame};
  }
  else return{};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2200199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[ C++ ] C++ 类和对象 -- 类的六个默认成员函数

目录 1.构造函数 2.析构函数 3.拷贝构造函数 4.赋值操作符重载 5.两个取地址操作符的重载 在C中当你创建一个空类&#xff0c;那这个空类是什么都没有吗&#xff1f;不是的&#xff0c;编译器会默认帮你生成六个成员函数 1.构造函数 构造函数是特殊的成员函数&#xff0c;…

使用数据库:

数据库&#xff1a; 1.为何需要数据库&#xff1f; 存储数据方法 第一种&#xff1a;用大脑记住数据&#xff0c; 第二种&#xff1a;写纸上&#xff0c; 第三种&#xff1a;写在计算机的内存中&#xff0c; 第四种&#xff1a;写出磁盘文件 2.数据库能做什么&#xff1…

【探索艺术新纪元:Midjourney中文版,让创意无界!】

&#x1f3a8; 艺术&#xff0c;从此触手可及 在这个数字时代&#xff0c;Midjourney中文版正引领一场艺术创作的革命。作为一款前沿的AI绘画工具&#xff0c;它利用深度学习技术&#xff0c;将你的想象转化为触手可及的艺术作品。无需深厚的绘画功底&#xff0c;只需简单的文…

机器学习:opencv--图像拼接

目录 前言 一、两个函数 1.显示图像 2.计算图片特征与描述符 二、代码实例 1.准备图像 2.特征检测 3.特征匹配 4.图像变换 5.图像融合 前言 图像拼接是一种将多张图像合成一幅大图的技术&#xff0c;常用于全景图生成、图像拼接和图像合成等应用场景。 一、两个函数…

第二十三天|回溯算法| 39. 组合总和,40. 组合总和II,131. 分割回文串

目录 39. 组合总和 未剪枝 剪枝优化 40. 组合总和II 131. 分割回文串 回溯 回溯动态规划优化回文串判断 今天的题目自己都没啥思路&#xff0c;二刷的时候再理解一下。尤其是131. 39. 组合总和 本题和77.组合 &#xff0c;216.组合总和III的区别是&#xff1a;本题没有…

IDEA 输入英文字体变了的问题

**问题&#xff1a;**有时不知道按了什么快捷键导致在 IDEA 输入英文字体变了&#xff0c;如下所示&#xff0c;看起来特别不顺眼&#xff1a; 出现以上问题是因为在输入时切换了中文输入法&#xff0c;并且在提示文字时按了 Shift 空格 键&#xff0c;导致出现以上字体变化情…

H、Happy Number(2024牛客国庆集训派对day7)

题目链接&#xff1a; H-Happy Number_2024牛客国庆集训派对day7 (nowcoder.com) 题目描述&#xff1a; 翻译为中文&#xff1a; 数据范围&#xff1a; 输入样例&#xff1a; 680 输出样例&#xff1a; 326623 分析: 本来以为是dfs&#xff0c;但是看到数据范围1e9, 联想到是…

uniapp自定义导航,全端兼容

我们在用uniapp 开发应用的时候&#xff0c;有的页面需要自定义导航&#xff0c; 1.如果普通的直接使用uni 扩展柜组件的 uni-nav-bar 也基本够用&#xff0c; 2.如果稍微带点自定义的这个值无法支持的&#xff0c;特别在小程序端&#xff0c;胶囊是会压住右边的按钮的 自定…

多模态简单了解

多模态 1.文本编码2. ViT图像编码器2.1图像矩阵self-attention计算&#xff1a; 3.Transformer多模态3.1CLIP 图文交互3.2 对比学习训练3.3 flamingo 图文交互3.4 LLava 图文交互 1.文本编码 简介&#xff1a; 即通过embedding将字符向量化&#xff0c;进入模型即可。 2. ViT…

水下图像增强(论文复现)

本文所涉及所有资源均在 传知代码平台 可获取。 目录 概述 一、论文思路 二、模型介绍&#xff1a; 三、实现方法 四、复现过程(重要) 部署方式 概述 2021年11月&#xff0c;提出一种用于水下图像增强的U型Transformer模型&#xff0c;这是首次在水下图像增强任务中使用Transfo…

InnoDB 磁盘结构 - RedoLog

文章目录 RedoLog是什么刷盘机制崩溃恢复相关参数Redo Log 和 Undo Log 对比 https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html RedoLog是什么 RedoLog 是MySQL的一种日志文件&#xff0c;用于在崩溃恢复期间纠正由不完整事务写入的数据。在正常操作过程中&…

AtCoder Beginner Contest 374

C - Separated Lunch 题目&#xff1a; 思路&#xff1a; dfs枚举每个数是否选入a数组中&#xff0c;求和比较 代码&#xff1a; #include <bits/stdc.h>using namespace std;typedef long long LL;const int N25;int a[N]; bool st[N]; int mn0x3f3f3f3f; int sum; …

VMWare安装ubuntu22虚拟机

1.下载VMware虚拟机和ubuntu 下载地址&#xff1a; VMware Workstation Pro - Download (softonic.com) Download Ubuntu Desktop | Ubuntu 2.Ubuntu的安装 1.VMware创建虚拟机。 2.选择默认即可点击下一步。 3.找到刚才下载的ubuntu20.04。选择下面的稍后安装操作系统。 …

众智OA办公系统 Account/Login SQL注入漏洞复现

0x01 产品简介 众智OA办公系统是一种专门为企业和机构的日常办公工作提供服务的综合性软件平台。它凭借先进的技术和人性化的设计理念,实现了信息的快速传递和自动化处理,帮助企业和机构实现信息化、自动化、智能化和标准化的办公管理。 0x02 漏洞概述 众智OA办公系统 Acc…

【命令操作】linux上watch命令详解 _ 统信 _ 麒麟 _ 方德

原文链接&#xff1a;【命令操作】linux上basename和dirname使用详解 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于Linux上watch命令的详解文章。watch命令是Linux系统中非常有用的一个工具&#xff0c;它可以定期执行指定的命令并在终端…

解决vscode cpptools-srv.exe占用内存过大,导致系统卡死问题

cpptools-srv.exe是安装了c扩展出来的进程。最新版本c扩展cpptools-srv.exe疯狂的占用内存&#xff0c;笔者机器64G内存 都被占满了&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; 网上也试了一些其他的办法&#xff0c;设置里面限制内存不过不好…

一入递归深似海,算法之美无止境

最近在刷leetcode hot100,在写二叉树中最大路径和的时候,看到了一个佬对递归的理解,深受启发,感觉自己对于递归的题又行了!!! 这里给大家分享一下(建立大家先去尝试一下这道题再来看 124. 二叉树中的最大路径和 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每…

【优选算法】--- 位运算

位运算 一、常见的位运算总结&#xff08;重点&#xff01;&#xff09;1、关于位运算的符号2、&#xff08;判断&#xff09;给一个数字n&#xff0c;确定它的二进制表示中的第X位&#xff0c;是1还是0&#xff1f;3、&#xff08;修改&#xff09;如何把一个二进制的数字的第…

算法:双指针系列(一)

双指针系列 一、移动零&#xff08;一&#xff09;题目分析&#xff08;二&#xff09;代码展示二、复写零&#xff08;一&#xff09;题目分析&#xff08;二&#xff09;代码展示三、快乐数&#xff08;一&#xff09;题目分析&#xff08;二&#xff09;代码展示&#xff08…

OceanBase 4.x 部署实践:如何从单机扩展至分布式部署

OceanBase 4.x 版本支持2种部署模式&#xff1a;单机部署与分布式部署&#xff0c;同时支持从单机平滑扩展至分布式架构。这样&#xff0c;可以有效解决小型业务向大型业务转型时面临的扩展难题&#xff0c;降低了机器资源的成本。 以下将详述如何通过命令行&#xff0c;实现集…