Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537502
  • 博文数量: 120
  • 博客积分: 3030
  • 博客等级: 中校
  • 技术积分: 1445
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-05 01:00
文章存档

2011年(1)

2009年(2)

2008年(32)

2007年(33)

2006年(52)

我的朋友

分类: LINUX

2008-04-09 10:20:17

最近在看CS8900datasheet和FS2410 TFTP源码,把源码全部注释了一遍,本来也就CS8900芯片的资料我能用得上,但是学习一下网络协议的简单实现也是一件高兴的事情。整理一下思路:

一、源码结构:

从底层到高层的顺序是:

CS8900.h   :定义了CS8900芯片内部寄存器地址,各寄存器的主要的位的掩码。

CS8900.c :定义了CS8900的基本操作:访问寄存器宏定义,检测(Probe),复位reset,初始化init,接收一帧RcvPkt,发送一帧TransmitPkt,作为查询方式的操作是否收到帧CS8900DBG_IsReceivedPacket。

mac.h     :声明了mac层的操作,这些操作在CS8900.c中实现。board_eth_init芯片初始化,board_eth_send发送一帧,board_eth_rcv接收一帧,这些函数是芯片基本函数的简单调用而已,board_eth_get_addr设置本地MAC地址。

skbuff.c/h : 定义和实现了关于缓冲区的操作。所有的协议的封装与拆解都是在sk_buff中实现的。这个数据结构在协议栈的实现中起了关键作用。

skbuf结构对应操作:skb_put,skb_push,skb_pull,alloc_skb,skb_reserve,主要用来申请缓冲,调整*data位置和len长度。

eth.c :这里定义了以太网物理层发送与接收数据帧的操作。主要调用mac.h中声明的函数去实现,但是参数发生改变,接收和发送的帧均以skb_buf的结构出现。有了skb_buf结构,协议封装拆解就有了非常方便的操作。见skbuf结构对应操作。从eth.c开始所有的操作都是协议的操作了。底层芯片CS8900那些操作已经全部被掩盖起来。

arp.h/arp.c :定义ARP头结构,ARP缓存,ARP收包,发送ARP请求包,添加到ARP缓存等操作。接收到ARP包时,如果是ARP请求且请求IP等于自身IP,发送ARP应答包。收到ARP包后把对方的IP、MAC对加入到ARP缓存中。发送ARP请求广播帧。

ip.h/ip.c :定义IP头结构体iphdr,定义IP包接收,IP包发送等。由于是简单实现,所以IP层没有实现分片等麻烦的操作。

icmp.h/icmp.c :定义icmp头结构体icmphdr,定义ICMP收包操作。ICMP只实现了简单的回显功能。即接收到ICMP echo request包后发送一个回显ICMP报文。其余的ICMP报文均不予处理。ICMP是基于IP协议的。

UDP.h/udp.c :定义UDP头结构体udphdr, UDP发送,UDP接收操作。UDP接收的时只处理目标端口为TFTP端口(0x45=69)的数据报,处理完UDP头后将数据交给TFTP接收函数。

tftp.h/tftp.c :定义TFTP头tftphdr结构,发送TFTP应答,收TFTP包,限于功能,收TFTP包的时候只处理WRQ和DATA两种操作码。因为源码功能只是要实现用TFTP下载文件到开发板上而已。

tftpput.h/tftpput.c :tftp接收开始,结束标志,tftp数据拷贝。

tftpmain.c : 整个TFTP工作流程。初始化物理层eth_init(); 初始化IP层(赋本地IP值);初始化ARP(所有缓存清0),把本地MAC,IP键加入ARP缓存。初始化接收开始标志,数据存入地址和长度变量 。 开始进入net_handle执行:申请一个sk_buf, 调用物理层eth_rcv接受一帧,去帧头,看上层协议是IP还是ARP,然后把用去掉帧头后剩下的包调用ip_rcv_packet或者arp_rcv_packet。

二、关于协议栈的执行流程

1.sk_buff的结构和操作:

struct sk_buff {

unsigned char pad[2];
unsigned char buf[ETH_FRAME_LEN];//buffer,这里是帧存储的位置
unsigned int truesize;    /* Buffer size       */

unsigned char *data;    /* Data head pointer     */这个指针总是指向当前层协议头在buf中的位置或者当前层协议数据部分在buf中的位置。
unsigned int len;    /* Length of actual data    */指示从*data位置到帧尾的length
};

buf[ETH_FRAME_LEN] 就是一帧实体,也是一帧协议栈的栈的实体。*data 是栈的指针,len则相当栈的底部,但是它是变化的,意义是*data到*data+len部分是当前协议层的内容(接收),或者这部分是已经填好的上层协议内容(发送)。 对应的操作有skb_push,skb_pull。

skb_push 用于从上层协议向下封装数据包,相当于压栈。char *skb_pull(struct sk_buff *skb, unsigned int ln) 就是要向栈中写入len字节前,先把栈指针*data-=ln, 而栈长len+=ln,返回当前*data指针,数据或者协议头(长度一定是ln)就可以往*data处填充了。很明显这是个向下生长的满栈 (FD) 。

skb_pull 用于从帧开始向上逐次解析协议。相当于弹栈的过程, char *skb_pull(struct sk_buff *skb, unsigned int ln)就是从栈中弹出ln个字节,*data+=ln, 栈长len-=ln,返回当前*data。弹出的ln字节就是下层协议已经处理过的协议头,返回值指向本层协议头,用它就可以开始解析本层协议了。

2、接收包的流程

1) 申请一个缓冲区sk_buf *skb, 以skb为参数调用以太网层的接收数据帧函数eth_rcv(skb)。eth_rcv(skb)调用board_eth_rcv(skb->data, &skb->len); board_eth_rcv调用CS8900 查询函数CS8900DBG_IsReceivedPacket()检查当前是否收到数据帧,如果收到调用RcvPkt((BYTE *)data, 1532);接收帧。 这样一帧数据就缓存到skb->buff中了。此时栈指针*data=buff,len=帧长。

2)处理以太网帧头,弹栈skb_pull(skb, ETH_HLEN)。以太网的帧头protocol解析上层协议是IP还是ARP,分别调用ip_rcv_packet(skb);(见4)或者arp_rcv_packet(skb);(见3)

3)如果是ARP报文,arp_rcv_packet(skb)处理ARP协议。判断ARP头的目标IP是否为本地IP,不是丢弃。是则判断ARP操作码是否为ARP请求,是发送ARP reply。缓存对方MAC IP 至ARP cache.当前帧的处理结束。

4)如果IP报文 ,ip_rcv_packet(skb); 处理IP层协议头,检查目的IP是否是本地IP,不是直接丢弃,是则根据IP头的上层协议是UDP还是ICMP,分别调用udp_rcv_packet(skb);或者icmp_rcv_packet(skb);

5)如果是ICMP数据包,icmp_rcv_packet(skb); 处理ICMP报文,如果ICMP头的类型是8,即请求回显,则发送一个回显ICMP报文。当前帧的处理结束。

6)如果是UDP数据包,udp_rcv_packet(skb)处理UDP协议头,检查目的端口是否为TFTP端口。由于程序只有TFTP作为UDP的上层协议。 如果是则弹栈后调用tftp_rcv_packet(skb);

7)如果是ftfp包,tftp_rcv_packet(skb);处理TFTP协议,限于功能,只处理WRQ和DATA两种请求,如果是WRQ请求,保存源IP和端口,发送一个ftfp应答block=0,开始block++计数并进入下载状态。

如果是DATA包,判断源IP和端口与上面保存的是否一致,当前tftp包的block号与block计数是否相等。相等则拷贝数据, 发送应答,block++,判断数据长是否小于512,是则表明block接收结束。当前tftp包的block号

3、发送包过程

1)sk_buff结构的另外两个操作,skb_put操作和skb_reserve(预留)操作:申请一个缓冲示进行任何操作的时候,*data=buff, len=0. 每层协议都有一个相对于下层协议头的偏移,这个偏移是一定的。skb_reserve(skb,len)操作是把skb->data+=len。每层协议都实现对应的reserve操作,它调用下层reserve,再把自己协议头的长度len添加到预留空间skb_reserve(skb,len)。

skb_put(skb,len)操作则是实现内容填充之后,skb->len+=len.

这两个操作和skb_push在发送数据包中起重要作用。如发送TFTP包,申请缓冲区,调用udp_skb_reserve(skb);把UDP头到以太网帧头的所有协议头的位置预留出来。再向*data处添加TFTP头和数据。

2)TFTP包发送过程

      发送TFTP应答,tftp_send_ack :申请缓冲,预留所有下层协议(UDP,IP,ETH)协议头空间,填写TFTP头和数据。调用udp_send(skb, client_ip, TFTP, client_port); 参数告诉下层相对的协议头怎么填。如TFTP,client_port 是给UDP协议层的,指定了本地端口和目标端口;client_ip则是给IP层的,给定目标IP。

      udp_send(skb, client_ip, TFTP, client_port); UDP层压栈后填写自己UDP协议头。调用ip_send(skb, ip, UDP); 指定目标IP和上层协议是UDP协议。

      IP层ip_send(skb, ip, UDP); 填写协议头,在这里注意校验和的计算。在ARP缓存中查找目标IP对应的MAC地址,查找成功然后调用下层eth_send(skb, dest_eth_addr, ETH_P_IP);查找失败发送ARP查询请求。

      ETH层 填充以太网帧头,调用底层驱动函数:board_eth_send(skb->data, skb->len);由CS8900芯片完成帧发送。

阅读(3306) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~