Lab 11:Networking

Lab :Networking

实验准备

在本次实验中要求编写网卡E1000 的传输和接受驱动程序,使其可以完成基本的操作。需要具备的知识基础主要是视频课程(80%)+作业提示(10%)+E1000技术手册(10%),必须理解网卡这类网络驱动程序是如何工作的,为此视频里讲解的很清楚,这里就不再描述了,其次是作业提示也能够很好地帮助我们完成题目的大部分情况,最后才是技术手册,由于这里技术手册特别晦涩难懂,我的建议是不如直接先看xv6的网络驱动程序源码,在不懂的话,尝试阅读技术手册。

接下来主要是对xv6里面的网卡驱动程序的剖析,其中最重要的是e1000_init.

首先是全局变量与宏声明

// xv6/kernel/e1000.c
#define TX_RING_SIZE 16
static struct tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(16)));
static struct mbuf *tx_mbufs[TX_RING_SIZE];
/* 上文定义一个传输环,环的组成元素是tx_desc类型,传输缓存层的元素类型是mbuf*
struct tx_desc   
{
  uint64 addr;//存放数据包的地址
  uint16 length;//数据包的长度
  //后面是校验位和控制位,可参考技术手册进行操作
  uint8 cso;
  uint8 cmd;
  uint8 status;
  uint8 css;
  uint16 special; 
};
控制符里面重要的是cmd和status,其中status表示上一个数据包是否传输成功,若成功则E1000_TXD_STAT_DD(0x01),否则为0x0.其次是cmd,它控制了该描述符的行为,当 E1000_TXD_CMD_EOP 被设置时,表示当前描述符为用于存储一块数据包的最后一个描述符(当数据包较大时可由多个描述符共同存储),当 E1000_TXD_CMD_RS 被设置时,硬件在提取当前描述符的内容时,会自动将该描述符的status 置为 E1000_TXD_STAT_DD 。
--------------------------------------------------------------------------------------
struct mbuf {                                                        
  struct mbuf  *next; // the next mbuf in the chain
  char         *head; // the current start position of the buffer
  unsigned int len;   // the length of the buffer
  char         buf[MBUF_SIZE]; // the backing store
};
*/
//接受环的编写与传输环大致相同
#define RX_RING_SIZE 16
static struct rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(16)));
static struct mbuf *rx_mbufs[RX_RING_SIZE];

// remember where the e1000's registers live.
static volatile uint32 *regs;//用于获取当前的(传输或者接收)环索引位置

struct spinlock e1000_lock;//这里为什么定义一个锁,而不是为传输和接收都定义一个锁很关键,主要是主机对网卡与网卡对主机的关系分别是多对一与一对多的关系,为此只需要在传输上面加锁即可使得多个用户进行调用网卡驱动程序。

接下来是传输的初始化过程

 // [E1000 14.5] Transmit initialization
  memset(tx_ring, 0, sizeof(tx_ring));
  for (i = 0; i < TX_RING_SIZE; i++) {
    tx_ring[i].status = E1000_TXD_STAT_DD;
    tx_mbufs[i] = 0;
  }
  regs[E1000_TDBAL] = (uint64) tx_ring;
  if(sizeof(tx_ring) % 128 != 0)
    panic("e1000");
  regs[E1000_TDLEN] = sizeof(tx_ring);
  regs[E1000_TDH] = regs[E1000_TDT] = 0;
/*
这一部分是对传输环的初始化,首先将所有值置零,接着将每个环的描述符状态设置为可用状态(E1000_TXD_STAT_DD),然后是传输缓存层指针设置为空指针,将当前的环索引置零,表示从头开始使用传输环,这里要和接收环做区别。
*/

接着是接收环的初始化过程

 // [E1000 14.4] Receive initialization
  memset(rx_ring, 0, sizeof(rx_ring));
  for (i = 0; i < RX_RING_SIZE; i++) {
    rx_mbufs[i] = mbufalloc(0);
    if (!rx_mbufs[i])
      panic("e1000");
    rx_ring[i].addr = (uint64) rx_mbufs[i]->head;
  }
  regs[E1000_RDBAL] = (uint64) rx_ring;
  if(sizeof(rx_ring) % 128 != 0)
    panic("e1000");
  regs[E1000_RDH] = 0;
  regs[E1000_RDT] = RX_RING_SIZE - 1;
  regs[E1000_RDLEN] = sizeof(rx_ring);
/* 
这一部分是对接收环的初始化,首先环置零,接着是为每个接收缓存申请一个可用的缓存地址,用于存放随时可能到达的数据包,接着将每个缓存地址放入环中。然后是对头部和尾部的初始化,主要的是E1000_RDT的定义要注意,这里他将其设置为最后一个元素的索引为此,后面使用前需要先将其加1在对其总长度取模。

接收环

实验指导部分

关于实现e1000_transmit的提示:

  • 首先,通过读取E1000_TDT控制寄存器,向E1000询问它期待下一个数据包的TX环索引。regs[E1000_TDT]
  • 然后检查环是否溢出。如果E1000_TXD_STAT_DD在E1000_TDT索引的描述符中没有设置,说明E1000还没有完成相应的前一个传输请求,所以返回错误。
  • 否则,使用 mbuffree() 释放从该描述符传输的最后一个 mbuf(如果有的话)。
  • 然后填入描述符。m->head指向内存中的数据包内容,m->len是数据包长度。设置必要的cmd标志(请看E1000手册的第3.3节),并把指向mbuf的指针藏起来,以便以后释放。
  • 最后,通过在E1000_TDT上加一来更新环形位置,并对TX_RING_SIZE进行调制。
  • 如果 e1000_transmit() 成功地将 mbuf 添加到环上,返回 0。在失败时(例如,没有可用的描述符来传输 mbuf),返回 -1 以便调用者知道释放 mbuf。

实现e1000_recv的一些提示。

  • 首先,通过获取E1000_RDT控制寄存器并在RX_RING_SIZE上加一个模数,向E1000询问下一个等待接收的数据包(如果有的话)所在的环形索引。
  • 然后通过检查描述符的状态部分的E1000_RXD_STAT_DD位来检查是否有新数据包。如果没有,停止。
  • 否则,将mbuf的m->len更新为描述符中报告的长度。使用 net_rx() 将 mbuf 传递给网络堆栈。
  • 然后使用 mbufalloc() 分配一个新的 mbuf 来替换刚刚给 net_rx() 的那个。将其数据指针 (m->head) 编入描述符。将描述符的状态位清除为零。
  • 最后,更新E1000_RDT寄存器,使其成为最后处理的环形描述符的索引。
  • e1000_init()用mbufs初始化RX环,你会想看看它是怎么做的,也许会借用代码。
  • 在某些时候,曾经到达的数据包总数将超过环的大小(16);确保你的代码能够处理这个问题。

你需要锁来应对xv6可能从多个进程中使用E1000,或者在中断到来时在内核线程中使用E1000的情况。

实验答案
int
e1000_transmit(struct mbuf *m)
{
  //
  // Your code here.
  //
  // the mbuf contains an ethernet frame; program it into
  // the TX descriptor ring so that the e1000 sends it. Stash
  // a pointer so that it can be freed after sending.
  //
  acquire(&e1000_lock);
  uint32 next_index = regs[E1000_TDT];
  if((tx_ring[next_index].status & E1000_TXD_STAT_DD) == 0){
      release(&e1000_lock);
      return -1;
  }
  if(tx_mbufs[next_index])
      mbuffree(tx_mbufs[next_index]);
  tx_ring[next_index].addr = (uint64)m->head;
  tx_ring[next_index].length = (uint16)m->len;
  tx_ring[next_index].cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
  tx_mbufs[next_index] = m;
  regs[E1000_TDT] = (next_index+1)%TX_RING_SIZE;
  release(&e1000_lock);
  return 0;
}

static void
e1000_recv(void)
{
  //
  // Your code here.
  //
  // Check for packets that have arrived from the e1000
  // Create and deliver an mbuf for each packet (using net_rx()).
  //
  uint32 next_index = (regs[E1000_RDT]+1)%RX_RING_SIZE;
  while(rx_ring[next_index].status & E1000_RXD_STAT_DD){
    if(rx_ring[next_index].length>MBUF_SIZE){
        panic("MBUF_SIZE OVERFLOW!");
    }
    rx_mbufs[next_index]->len = rx_ring[next_index].length;
    net_rx(rx_mbufs[next_index]);
    rx_mbufs[next_index] =  mbufalloc(0);
    rx_ring[next_index].addr = (uint64)rx_mbufs[next_index]->head;
    rx_ring[next_index].status = 0;
    next_index = (next_index+1)%RX_RING_SIZE;
  }
  regs[E1000_RDT] = (next_index-1)%RX_RING_SIZE;
}

测试结果
== Test running nettests == 
$ make qemu-gdb
(3.4s) 
== Test   nettest: ping == 
  nettest: ping: OK 
== Test   nettest: single process == 
  nettest: single process: OK 
== Test   nettest: multi-process == 
  nettest: multi-process: OK 
== Test   nettest: DNS == 
  nettest: DNS: OK 
== Test time == 
time: OK 
Score: 100/100

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
The CCNA® Voice certification expands your CCNA-level skill set to prepare for a career in voice networking. This lab manual helps to prepare you for the Introducing Cisco Voice and Unified Communications Administration (ICOMM v8.0) certification exam (640-461). CCNA Voice Lab Manual gives you extensive hands-on practice for developing an in-depth understanding of voice networking principles, tools, skills, configurations, integration challenges, and troubleshooting techniques. Using this manual, you can practice a wide spectrum of tasks involving Cisco Unified Communications Manager, Unity Connection, Unified Communications Manager Express, and Unified Presence. CCNA Voice Lab Manual addresses all exam topics and offers additional guidance for successfully implementing IP voice solutions in small-to-medium-sized businesses. CCNA Voice 640-461 Official Exam Certification Guide, Second Edition ISBN-13: 978-1-58720-417-3 ISBN-10: 1-58720-417-7 CCNA Voice Portable Command Guide ISBN-13: 978-1-58720-442-5 ISBN-10: 1-58720-442-8 Configuring Cisco Unified Communications Manager and Unity Connection: A Step-by-Step Guide, Second Edition ISBN-13: 978-1-58714-226-0 ISBN-10: 1-58714-226-0 CCNA Voice Quick Reference ISBN-13: 978-1-58705-767-0 ISBN-10: 1-58705-767-0 Table of Contents Part I: Voice Concepts Chapter 1. Traditional and Unified Voice Chapter 2. Understanding Cisco Unified Communications Solutions Chapter 3. Establishing Network Connectivity and Understanding IP Phone Registration Part II: Cisco Unified Communications Manager Express (CUCME) Chapter 4. Introduction to CUCME Administration Chapter 5. Configuring Phones and Users in Cisco Unified Communications Manager Express (CUCME) Chapter 6. Cisco Unified Communications Manager Express (CUCME) Dial Plans and QoS Chapter 7. Cisco Unified Communications Manager Express (CUCME) Productivity Features Part III: Cisco Unified Communications Manager (CUCM) Chapter 8. Administrative Interfaces Chapter 9. Managing Services, Phones, and Users Chapter 10. CUCM Dial Plan Chapter 11. CUCM Telephony Features Chapter 12. CUCM Mobility Features Part IV: Voicemail and Presence Solutions Chapter 13. Cisco Unity Connection Chapter 14. Cisco Unified Presence Part V: Management and Troubleshooting Chapter 15. Troubleshooting Cisco Unified Communications Manager Express (CUCME) Chapter 16. Troubleshooting CUCM Chapter 17. Monitoring Cisco Unity Connection Part VI: Appendixes Appendix A. IP Addressing and Phone Extensions for Cisco Unified Communications Manager Express (CUCME) Labs Appendix B. IP Addressing and Phone Extensions for Cisco Unified Communications Manager (CUCM) Labs (Chapters 8–14 and 16–17) Appendix C. Building Cisco Unified Communications Manager (CUCM), Cisco Unity Connection (CUC), and Cisco Unified Presence (CUPS) Servers in VMware Workstation Appendix D. Configuring the Cisco Unified Communications Manager Express Router Appendix E. PSTN Simulator
### 回答1: 使用以下代码: let socket_addr: std::net::SocketAddr = // ...; let win_socket_addr: windows_sys::Win32::Networking::WinSock::SOCKADDR_IN = socket_addr.into(); ### 回答2: 要将std::net::SocketAddr转化为windows_sys::Win32::Networking::WinSock::SOCKADDR_IN,可以使用以下Rust代码: ```rust use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use winapi::um::winsock2::SOCKADDR_IN; use winapi::shared::ws2def::{AF_INET, AF_INET6}; use winapi::shared::in6addr::in6addr_any; fn socket_addr_to_sockaddr_in(socket_addr: &SocketAddr) -> SOCKADDR_IN { let ip_addr = socket_addr.ip(); let port = socket_addr.port(); match ip_addr { IpAddr::V4(ipv4_addr) => { let mut sockaddr_in: SOCKADDR_IN = unsafe { std::mem::zeroed() }; sockaddr_in.sin_family = AF_INET as u16; sockaddr_in.sin_addr.S_un.S_addr = u32::from(ipv4_addr); sockaddr_in.sin_port = port.to_be(); sockaddr_in } IpAddr::V6(ipv6_addr) => { let mut sockaddr_in6: SOCKADDR_IN = unsafe { std::mem::zeroed() }; sockaddr_in6.sin6_family = AF_INET6 as u16; sockaddr_in6.sin6_port = port.to_be(); sockaddr_in6.sin6_addr = *ipv6_addr.segments(); if *ipv6_addr == Ipv6Addr::from(in6addr_any) { sockaddr_in6.sin6_addr = *in6addr_any; } sockaddr_in6 } } } fn main() { let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); let sockaddr_in = socket_addr_to_sockaddr_in(&socket_addr); // Do something with the converted SOCKADDR_IN println!( "Converted SOCKADDR_IN: {{ family: {}, addr: {}, port: {} }}", sockaddr_in.sin_family, sockaddr_in.sin_addr.S_un.S_addr, sockaddr_in.sin_port ); } ``` 以上代码使用Rust的winapi库来访问Windows Socket API,并将std::net::SocketAddr的IP地址和端口转换为windows_sys::Win32::Networking::WinSock::SOCKADDR_IN结构体。根据输入的std::net::SocketAddr类型,使用IF_INET或IF_INET6来设置SOCKADDR_IN的family字段。然后将IP地址和端口填充到SOCKADDR_IN结构体中。在这个例子中,我们将std::net::SocketAddr转化为127.0.0.1:8080的SOCKADDR_IN。 ### 回答3: 使用Rust编写的代码如下所示: ```rust use std::net::SocketAddr; use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN; fn main() { let socket_addr = SocketAddr::new("127.0.0.1".parse().unwrap(), 8080); let sockaddr_in = SOCKADDR_IN { sin_family: 2, // AF_INET sin_port: socket_addr.port().to_be(), sin_addr: socket_addr.ip().octets().into(), sin_zero: [0; 8], // initialize sin_zero with zeros }; println!("SOCKADDR_IN: {:?}", sockaddr_in); } ``` 以上代码首先导入了`std::net::SocketAddr`和`windows_sys::Win32::Networking::WinSock::SOCKADDR_IN`模块。然后创建了一个`SocketAddr`实例,该实例包含了IP地址和端口号。接下来,我们定义了一个`SOCKADDR_IN`结构体变量,并根据`SocketAddr`的信息进行初始化。最后,打印输出了转换后的`SOCKADDR_IN`实例。 这段代码将`std::net::SocketAddr`转化为了`windows_sys::Win32::Networking::WinSock::SOCKADDR_IN`。在转换过程中,我们将IP地址转化为字节数组,然后将端口号进行了字节序的转换。最后,我们打印输出了转换后的结果,以便验证转换是否成功。 注意:本示例中使用的`sockets` crate中的`windows-sys`模块提供了便于与Windows系统交互的Rust绑定,以简化开发过程。请在使用之前确认已在`Cargo.toml`中添加相应的依赖。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值