Lab: networking
lab 地址
还可以参考的在线课程: 21 (介绍了网络)和24(介绍了作业推荐只看前面然后做了作业可以看后面部分)
https://www.bilibili.com/video/BV19k4y1C7kA?p=20&vd_source=f390d3ec07fdc69957679b24c75ca379
还有一定要按照它的要求去看下手册:
Browse the E1000 Software Developer’s Manual. This manual covers several closely related Ethernet controllers. QEMU emulates the 82540EM. Skim Chapter 2 now to get a feel for the device. To write your driver, you’ll need to be familiar with Chapters 3 and 14, as well as 4.1 (though not 4.1’s subsections). You’ll also need to use Chapter 13 as a reference. The other chapters mostly cover components of the E1000 that your driver won’t have to interact with. Don’t worry about the details at first; just get a feel for how the document is structured so you can find things later. The E1000 has many advanced features, most of which you can ignore. Only a small set of basic features is needed to complete this lab.
这个课程其实只是看上去很大。但是实现起来没有那么复杂。
主要明白下面几个概念:
- 对于网卡层发送和接收,它都有一个 环形的buffer 读对应 rx ring,写 tx ring 。
- 发送:当有数据发送到网卡的缓冲区时网卡试图找到空闲的一个硬件地址(ring buffer),然后将缓冲区地址写入到该硬件地址中(寄存器中)。网卡就可以直接通过该地址直接读取要发送的数据进行发送。
- 接收:当网卡收到数据时这个时候硬件将数据从物理层(网卡)发送到网络层。(在该作业中硬件数据已经被软件正确放置到rx_mbuf中)
发送:
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.
//
// lock
acquire(&e1000_lock);
// 1. check tx ring read index from tx ring 如果E1000_TDT中有数据则E1000_TXD_STAT_DD 被设置
uint32 tail= regs[E1000_TDT];
struct tx_desc *desc= &tx_ring[tail];
//Then check if the the ring is overflowing. If E1000_TXD_STAT_DD is not set in the descriptor indexed by E1000_TDT,
// the E1000 hasn't finished the corresponding previous transmission request, so return an error.
if( ! desc->status & E1000_TXD_STAT_DD ){
release(&e1000_lock);
return -1;
}
//Otherwise, use mbuffree() to free the last mbuf that was transmitted from that descriptor (if there was one).
if(tx_mbufs[tail]){
mbuffree(tx_mbufs[tail]);
}
//Then fill in the descriptor.
// m->head points to the packet's content in memory,
desc->addr=(uint64)m->head;
//and m->len is the packet length
desc->length=m->len;
// Set the necessary cmd flags
//EOP (bit 0)
// End Of Packet
// When set, indicates the last descriptor making up the packet. One or many
// descriptors can be used to form a packet.
//RS (bit 3)
// Report Status
// When set, the Ethernet controller needs to report the status information. This ability
// may be used by software that does in-memory checks of the transmit descriptors to
// determine which ones are done and packets have been buffered in the transmit
// FIFO. Software does it by looking at the descriptor status byte and checking the
// Descriptor Done (DD) bit.
desc->cmd= E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
// stash away a pointer to the mbuf for later freeing.
tx_mbufs[tail]=0;
regs[E1000_TDT]= (tail+1) % TX_RING_SIZE;
release(&e1000_lock);
return 0;
}
接收:
static void
e1000_recv(void)
{
// why read don't need use lock cuz read method called by trap. So no concurrent condition with recv
//
// Your code here.
//
// Check for packets that have arrived from the e1000
// Create and deliver an mbuf for each packet (using net_rx()).
//
//First ask the E1000 for the ring index at which the next waiting received packet (if any) is located,
//by fetching the E1000_RDT control register and adding one modulo RX_RING_SIZE.
while(1){
uint32 tail=(regs[E1000_RDT]+1) % RX_RING_SIZE;
struct rx_desc *desc = &rx_ring[tail];
//Then check if a new packet is available
// by checking for the E1000_RXD_STAT_DD bit in the status portion of the descriptor. If not, stop.
if(! (desc->status & E1000_RXD_STAT_DD) ){
return;
}
//Otherwise, update the mbuf's m->len to the length reported in the descriptor. Deliver the mbuf to the network stack using net_rx().
rx_mbufs[tail]->len= desc->length;
net_rx(rx_mbufs[tail]);
//Then allocate a new mbuf using mbufalloc() to replace the one just given to net_rx().
rx_mbufs[tail] = mbufalloc(0);
//Program its data pointer (m->head) into the descriptor.
desc->addr= (uint64)rx_mbufs[tail]->head;
//Clear the descriptor's status bits to zero.
desc->status=0;
//Finally, update the E1000_RDT register to be the index of the last ring descriptor processed.
regs[E1000_RDT]=(regs[E1000_RDT]+1)% RX_RING_SIZE;
}
}
测试须知:
nettests 测试时 它的dns主机是 8.8.8.8
在国内的话是连不上的,可以切换它的测试源用223.5.5.5 代替
static void
dns()
{
#define N 1000
uint8 obuf[N];
uint8 ibuf[N];
uint32 dst;
int fd;
int len;
memset(obuf, 0, N);
memset(ibuf, 0, N);
// 8.8.8.8: google's name server
//223.5.5.5
// dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0);
dst = (223 << 24) | (5 << 16) | (5 << 8) | (5 << 0);
if((fd = connect(dst, 10000, 53)) < 0){
fprintf(2, "ping: connect() failed\n");
exit(1);
}
结果:
$ nettests
nettests running on port 25600
testing ping: OK
testing single-process pings: OK
testing multi-process pings: OK
testing DNS
DNS arecord for pdos.csail.mit.edu. is 128.52.129.126
DNS OK
all tests passed.