一.内核会在每层上都把流量传送至正确的协议,就是调用该协议所注册的处理程序函数。IP协议注册的处理程序函数就是ip_rcv函数(可以查看我写的《IP协议注册流程》)。
- /*
- * Main IP Receive routine.
- */
- int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
- {
- struct iphdr *iph;
- /* When the interface is in promisc. mode, drop all the crap
- * that it receives, do not try to analyse it.
- * 如果数据帧的L2的目的地址和接收接口的地址不同时,
- * skb->pkt_type就会设置为 PACKET_OTHERHOST。
- * 通常情况下都会被NIC本身丢弃
- */
- if (skb->pkt_type == PACKET_OTHERHOST)
- goto drop;
- IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);
- /*
- * skb的引用计数是否大于1?
- * 如果大于1,克隆一份缓冲区副本使其可以修改其封包
- *
- */
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
- goto out;
- }
- /*
- * 判断skb->data所指区域的数据区块至少和IP报头一样大
- * sizeof(struct iphdr) <= skb_headlen(skb)
- */
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
- goto inhdr_error;
- /*
- * skb空间可能会被pskb_may_pull改变
- * iph需要重新赋值
- */
- iph = skb->nh.iph;
- /*
- * 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, skip loopback checksums]
- * 4. Doesn't have a bogus length
- */
- /* 5*4 == ip header ,IP头至少为20个字节 */
- if (iph->ihl < 5 || iph->version != 4)
- goto inhdr_error;
- /*
- * 完整的IP报头大小:ip header length + ip options length
- */
- if (!pskb_may_pull(skb, iph->ihl*4))
- goto inhdr_error;
- iph = skb->nh.iph;
- if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
- goto inhdr_error;
- {
- __u32 len = ntohs(iph->tot_len);
- if (skb->len < len || len < (iph->ihl<<2))
- goto inhdr_error;
- /* Our transport medium may have padded the buffer out. Now we know it
- * is IP we can trim to the true length of the frame.
- * Note this now means skb->len holds ntohs(iph->tot_len).
- */
- if (skb->len > len) {
- __pskb_trim(skb, len);
- /*
- * __pskb_trim会修改skb,
- * 硬件支持校验的话,就会出错
- * 所以重置为 CHECKSUM_NONE
- */
- if (skb->ip_summed == CHECKSUM_HW)
- skb->ip_summed = CHECKSUM_NONE;
- }
- }
- return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
- ip_rcv_finish);
- inhdr_error:
- IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
- drop:
- kfree_skb(skb);
- out:
- return NET_RX_DROP;
- }
二.ip_rcv所做的仅是封包的基本健康检查。真正数据包操作是ip_rcv_finish完成的。
- static inline int ip_rcv_finish(struct sk_buff *skb)
- {
- struct net_device *dev = skb->dev;
- struct iphdr *iph = skb->nh.iph;
- /*
- * skb->dst包含目的地的路由信息。
- * 如果没有包含目的地的路由信息,就会调用ip_route_input()。
- * 询问路由子系统往哪里发送,如果路由子系统没找到路由,就丢弃skb。
- */
- if (skb->dst == NULL) {
- if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
- goto drop;
- }
- ...省略
- if (iph->ihl > 5) {
- struct ip_options *opt;
- /* It looks as overkill, because not all
- IP options require packet mangling.
- But it is the easiest for now, especially taking
- into account that combination of IP options
- and running sniffer is extremely rare condition.
- --ANK (980813)
- */
- if (skb_cow(skb, skb_headroom(skb))) {
- IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
- goto drop;
- }
- iph = skb->nh.iph;
- /*
- * 解析报头中携带的IP选项
- * 如果封包是由来源地路由的话,
- * 则需要分析选项是否配置“严格和松散路由选项”
- */
- if (ip_options_compile(NULL, skb))
- goto inhdr_error;
- opt = &(IPCB(skb)->opt);
- if (opt->srr) {
- /*
- * 以下操作主要判断数据包是发往本地还是转发
- * 对skb->dst(路由信息)进行赋值操作
- */
- struct in_device *in_dev = in_dev_get(dev);
- if (in_dev) {
- if (!IN_DEV_SOURCE_ROUTE(in_dev)) {
- if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
- printk(KERN_INFO "source route option %u.%u.%u.%u -> %u.%u.%u.%u\n",
- NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
- in_dev_put(in_dev);
- goto drop;
- }
- /* 转发还是本地发送 */
- in_dev_put(in_dev);
- }
- if (ip_options_rcv_srr(skb))
- goto drop;
- }
- }
- /*
- * dst_input(存储与skb->dst->input)会根据IP包是发送本地还是转发
- * 发往本地(dst_input等于ip_local_deliver)调用ip_local_deliver
- * 转发调用ip_forward
- */
- return dst_input(skb);
- inhdr_error:
- IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
- drop:
- kfree_skb(skb);
- return NET_RX_DROP;
- }
三. 选项处理
1.struct ip_options结构体:
- struct ip_options表示IP选项
- struct ip_options {
- __be32 faddr;
- unsigned char optlen;
- unsigned char srr;
- unsigned char rr;
- unsigned char ts;
- unsigned char is_data:1,
- is_strictroute:1,
- srr_is_hit:1,
- is_changed:1,
- rr_needaddr:1,
- ts_needtime:1,
- ts_needaddr:1;
- unsigned char router_alert;
- unsigned char cipso;
- unsigned char __pad2;
- unsigned char __data[0];
- };
- faddr用于在IPOPT_LSRR和IPOPT_SSRR选项时,记录下一站的IP地址
- optlen是IP首部中选项所占据的长度
- srr记录IPOPT_LSRR或IPOPT_SSRR选项在IP首部中的偏移量(选项的第一个字节的地址减去IP首部的第一个 字节的地址)
- rr用于记录 IPOPT_RR选项在IP首部中的偏移量
- ts用于记录IPOPT_TIMESTAMP选项在IP首部中的偏移量
- is_data表示该IP选项拥有数据,选项数据存放在__data中
- is_strictroute表示该选项是IPOPT_SSRR,而不是IPOPT_LSRR
- srr_is_hit跟源站路由选项相关,对IP选项作过任何的修改,需要把is_changed置1
- rr_needaddr表示有IPOPT_RR选项,需要记录IP地址
- ts_needtime表示IPOPT_TIMESTAMP选项需要记录时间戳
- ts_needaddr表示IPOPT_TIMESTAMP选项需要记录IP地址
- router_alert用于IPOPT_RA选项
2.ip_options_compile函数主要是分析skb中的IP选项。
- int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
- {
- int l;
- unsigned char * iph;
- unsigned char * optptr;
- int optlen;
- unsigned char * pp_ptr = NULL;
- struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL;
- if (!opt) {
- /* opt == NULL 处理入口的封包 */
- opt = &(IPCB(skb)->opt);
- memset(opt, 0, sizeof(struct ip_options));
- iph = skb->nh.raw;
- opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
- optptr = iph + sizeof(struct iphdr);
- opt->is_data = 0;
- } else {
- /* opt != NULL 封包正被传输 */
- optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
- iph = optptr - sizeof(struct iphdr);
- }
- for (l = opt->optlen; l > 0; ) {
- switch (*optptr) {/* 单字节选项 */
- case IPOPT_END:/* 当IP选项不是4字节倍数时,以IPOPT_END选项为IP报头补白 */
- for (optptr++, l--; l>0; optptr++, l--) {
- if (*optptr != IPOPT_END) {
- *optptr = IPOPT_END;
- opt->is_changed = 1;
- }
- }
- goto eol;
- case IPOPT_NOOP:/* 填补选项之间的空白 */
- l--;
- optptr++;
- continue;
- }
- optlen = optptr[1];
- if (optlen<2 || optlen>l) {
- pp_ptr = optptr;
- goto error;
- }
- switch (*optptr) {/* 多字节选项 */
- case IPOPT_SSRR:
- case IPOPT_LSRR:
- if (optlen < 3) {
- pp_ptr = optptr + 1;
- goto error;
- }
- if (optptr[2] < 4) { /* optptr[2]存放的偏移量 */
- pp_ptr = optptr + 2;
- goto error;
- }
- /* NB: cf RFC-1812 5.2.4.1 */
- if (opt->srr) {
- pp_ptr = optptr;
- goto error;
- }
- if (!skb) {/* skb == NULL解析一个由本地产生的外出封包 */
- if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
- pp_ptr = optptr + 1;
- goto error;
- }
- /* &optptr[3]表示第一个IP地址的指针 */
- memcpy(&opt->faddr, &optptr[3], 4);
- if (optlen > 7)
- memmove(&optptr[3], &optptr[7], optlen-7);
- }
- /* optptr[0] == IPOPT_SSRR表示严格路由否则为松散路由 */
- opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
- opt->srr = optptr - iph;
- break;
- case IPOPT_RR:/* Record Route选项 */
- if (opt->rr) {
- pp_ptr = optptr;
- goto error;
- }
- if (optlen < 3) {
- pp_ptr = optptr + 1;
- goto error;
- }
- if (optptr[2] < 4) {
- pp_ptr = optptr + 2;
- goto error;
- }
- if (optptr[2] <= optlen) {
- if (optptr[2]+3 > optlen) {
- pp_ptr = optptr + 2;
- goto error;
- }
- if (skb) {/* 处理入口封包 */
- memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
- opt->is_changed = 1;/* 重新计算校验和 */
- }
- optptr[2] += 4; /* 偏移加上4 */
- opt->rr_needaddr = 1; /* 还有空间,继续把IP地址写入IP报头 */
- }
- opt->rr = optptr - iph;
- break;
- case IPOPT_TIMESTAMP:
- if (opt->ts) {
- pp_ptr = optptr;
- goto error;
- }
- if (optlen < 4) {
- pp_ptr = optptr + 1;
- goto error;
- }
- if (optptr[2] < 5) {
- pp_ptr = optptr + 2;
- goto error;
- }
- if (optptr[2] <= optlen) {
- __u32 * timeptr = NULL;
- if (optptr[2]+3 > optptr[1]) {
- pp_ptr = optptr + 2;
- goto error;
- }
- switch (optptr[3]&0xF) {/* 低四位表示子命令 */
- case IPOPT_TS_TSONLY:
- opt->ts = optptr - iph;
- if (skb)
- timeptr = (__u32*)&optptr[optptr[2]-1];
- opt->ts_needtime = 1;
- optptr[2] += 4;
- break;
- case IPOPT_TS_TSANDADDR:
- if (optptr[2]+7 > optptr[1]) {
- pp_ptr = optptr + 2;
- goto error;
- }
- opt->ts = optptr - iph;
- if (skb) {
- memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
- timeptr = (__u32*)&optptr[optptr[2]+3];
- }
- opt->ts_needaddr = 1;
- opt->ts_needtime = 1;
- optptr[2] += 8;
- break;
- case IPOPT_TS_PRESPEC:
- if (optptr[2]+7 > optptr[1]) {
- pp_ptr = optptr + 2;
- goto error;
- }
- opt->ts = optptr - iph;
- {
- u32 addr;
- memcpy(&addr, &optptr[optptr[2]-1], 4);
- if (inet_addr_type(addr) == RTN_UNICAST)
- break;
- if (skb)
- timeptr = (__u32*)&optptr[optptr[2]+3];
- }
- opt->ts_needtime = 1;
- optptr[2] += 8;
- break;
- default:
- if (!skb && !capable(CAP_NET_RAW)) {
- pp_ptr = optptr + 3;
- goto error;
- }
- break;
- }
- if (timeptr) {/* 写入时间戳并更新校验和 */
- struct timeval tv;
- __u32 midtime;
- do_gettimeofday(&tv);
- midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
- memcpy(timeptr, &midtime, sizeof(__u32));
- opt->is_changed = 1;
- }
- } else {/* 是否溢出 */
- unsigned overflow = optptr[3]>>4;
- if (overflow == 15) {
- pp_ptr = optptr + 3;
- goto error;
- }
- opt->ts = optptr - iph;
- if (skb) {
- optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
- opt->is_changed = 1;
- }
- }
- break;
- case IPOPT_RA:
- if (optlen < 4) {
- pp_ptr = optptr + 1;
- goto error;
- }
- if (optptr[2] == 0 && optptr[3] == 0)
- opt->router_alert = optptr - iph;
- break;
- case IPOPT_SEC:
- case IPOPT_SID:
- default:
- if (!skb && !capable(CAP_NET_RAW)) {
- pp_ptr = optptr;
- goto error;
- }
- break;
- }
- l -= optlen;
- optptr += optlen;
- }
- eol:
- if (!pp_ptr)
- return 0;
- error:
- if (skb) {
- icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
- }
- return -EINVAL;
- }
阅读(4057) | 评论(0) | 转发(0) |