日期:2014-05-16 浏览次数:20930 次
接上面两篇:点击打开链接
点击打开链接
先看看ip头结构:
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) // 小端 __u8 ihl:4, // 首部长度(4位):首部长度指的是IP层头部占32 bit字的数目(也就是IP层头部包含多少个4字节 -- 32位),包括任何选项 version:4; // 版本(4位),目前的协议版本号是4,称作IPv4。(注意头最长是:当4位全部取1即1111=15,那么15*32/8=60B) #elif defined (__BIG_ENDIAN_BITFIELD) // 大端:调换位置 __u8 version:4, // 因为这两个字段共享一个字节,所以必须要区分大小端 ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif __u8 tos; // 服务类型字段(8位): 服务类型(TOS)字段包括一个3 bit的优先权子字段,4 bit的TOS子字段和1 bit未用位但必须置0。4 bit的TOS子字段分别代表:最小时延、最大吞吐量、最高可靠性和最小费用。4 bit中只能设置其中1 bit。如果所有4 bit均为0,那么就意味着是一般服务。 __be16 tot_len; // 总长度字段(16位)是指整个IP数据报的长度,以字节为单位。 __be16 id; // 标识字段(16位)唯一地标识主机发送的每一份数据报。通常每发送一份报文它的值就会加1。 __be16 frag_off; // frag_off域的低13位 -- 分段偏移(Fragment offset)域指明了该分段在当前数据报中的什么位置上。除了一个数据报的最后一个分段以外,其他所有的分段(分片)必须是8字节的倍数。这是8字节是基本分段单位。iphdr->frag_off的高3位(1) 比特0是保留的,必须为0;(2) 比特1是“更多分片”(MF -- More Fragment)标志。除了最后一片外,其他每个组成数据报的片都要把该比特置1。 (3) 比特2是“不分片”(DF -- Don't Fragment)标志,如果将这一比特置1,IP将不对数据报进行分片,这时如果有需要进行分片的数据报到来,会丢弃此数据报并发送一个ICMP差错报文给起始端。 __u8 ttl; // 生存时间字段设置了数据报可以经过的最多路由器数 __u8 protocol; // 协议字段(8位):指明了该将它交给哪个传输进程。TCP是一种可能,但是UDP或者其他的协议也是可能的。 __sum16 check; // 首部检验和字段(16位)是根据IP首部计算的检验和码。它不对首部后面的数据进行计算。 __be32 saddr; // 源IP地址 __be32 daddr; // 目的ip地址 /*The options start here. */ // };
下面从开始ip_rcv函数开始:
/* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct iphdr *iph; u32 len; if (dev->nd_net != &init_net) // 看一下初始化的网络空间是不是包含这个设备,不匹配则丢包 goto drop; /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. *///注意当包类型是PACKET_OTHERHOST时候,上一层就会直接丢掉所有的包,如果网卡此时被设置为promisc混杂模式,此时包就会传递到3层, 这个时侯内核会有hook函数来处理这个,而这里就只需要直接丢掉所有的包! if (skb->pkt_type == PACKET_OTHERHOST) // 类型查看在if_packet.h中 goto drop; IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES); // 给需要重组的碎片计数 if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { // 检查缓冲区skb是不是共享的!如果共享,克隆一个返回给skb,如果不是原skb返回! IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS); // 如果为NULL,那么返回内存分配失败! goto out; // 检查是否共享就是检查是不是超过一个人引用了这个skb~~~ } // 注意参数是skb和ip的头长度,如果skb比头长度还小,肯定哪里出错了!直接error,否则ok~ if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; iph = ip_hdr(skb); // 得到ip头(具体放入ip的头看上面的分析) /* * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. * * Is the datagram acceptable? * * 1. Length at least the size of an ip header * 2. Version of 4 * 3. Checksums correctly. [Speed optimisation for later, ski