Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1347821
  • 博文数量: 244
  • 博客积分: 10311
  • 博客等级: 上将
  • 技术积分: 3341
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-14 21:50
文章分类

全部博文(244)

文章存档

2013年(6)

2012年(5)

2011年(16)

2010年(11)

2009年(172)

2008年(34)

分类: 系统运维

2009-06-18 15:20:53

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芯片完成帧发送。
 
阅读(1855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~