荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: jjk (UNIX+C+XML+?? 傻了?), 信区: Linux
标  题: IP的接收函数分析(转寄)
发信站: 荔园晨风BBS站 (Mon Apr 22 19:49:05 2002), 转信

【 以下文字转载自 jjk 的信箱 】
【 原文由 jjk.bbs@apue.dhs.org 所发表 】
发信人: tl (tl), 信区: UKP
标  题: IP的接收函数分析
发信站: UNIX编程 (2002年04月16日21:00:11 星期二), 转信

作者:硅谷农民<mailto:ggnm@kerneldiary.net>

 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
 *pt)
    当系统接收到网络包的时候,如果是IP包,则调用ip_rcv来处理。ip_rcv主要的功
能是碎片重组,
根据路由将将它们送给上一层协议栈(例如tcp,udp)处理,或者调用ip_forward将IP
包转送给另外
一个网卡设备。
实现过程:
 * 检查skb有没有被另外的进程调动,如果有的话,则克隆(clone)一个同样的skb。

 * 检查IP包的基本属性:
    1. IP头的长度是以4字节为单位计算的,标准的IP头部是
    20字节,所以iph->ihl 必须大于或等于5。
    2. IP包的版本为IPv4。
    3. 调用ip_fast_csum来检查校验。
    4. 检查IP包的总长度ntohs(iph->tot_len)和skb缓冲区的长度是不是匹配;IP包的
长度至少大于
    IP头(IP header)的长度(iph->ihl<<2)。
 * 如果skb的长度大于IP包的长度的话,说明可能网卡自动附加的一些空字符在缓冲区
的后面,则调用
   __pskb_trim(skb, len)将多余的字符去掉。
 * 当上面的步骤完成以后,检查需不需要调用netfilter的挂钩函数(hook function),
完成以后,调用
   ip_rcv_finish继续进行IP协议的处理。
        int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
t_type *pt)
        {
             ... ...
                if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
                        goto out;
                if (!pskb_may_pull(skb, sizeof(struct iphdr)))
                        goto inhdr_error;
                iph = skb->nh.iph;
                if (iph->ihl < 5 || iph->version != 4)
                        goto inhdr_error;
                if (!pskb_may_pull(skb, iph->ihl*4))
                        goto inhdr_error;
                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;
                        if (skb->len > len) {
                                __pskb_trim(skb, len);
                                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);
        }
----------------------------------------------------------------------------
---------
static inline int ip_rcv_finish(struct sk_buff *skb)
实现过程:
 * 调用ip_route_input根据IP源地址,目的地址计算出这个IP包的路由,存放到skb->
dst中,
  这个路由决定了IP包如何在网络上传输(例如根据子网转发到不同的网卡设备等等
)。
        static inline int ip_rcv_finish(struct sk_buff *skb)
        {
                struct net_device *dev = skb->dev;
                struct iphdr *iph = skb->nh.iph;
                if (skb->dst == NULL) {
                        if (ip_route_input(skb, iph->daddr, iph->saddr, iph-
>tos, dev))
                                goto drop;
                }
 * 如果iph->ihl > 5 (IP头部长度大于20),表示这个IP包有其他选项(option),则调

   ip_options_compile填好各个选项IPCB(skb)->opt,这里之处理了“源路由”选项(
source route),
   具体细节这里先略过。
                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)))
                                goto drop;
                        iph = skb->nh.iph;
                        skb->ip_summed = 0;
                        if (ip_options_compile(NULL, skb))
                                goto inhdr_error;
                        opt = &(IPCB(skb)->opt);
                        if (opt->srr) {
                                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) && n
et_ratelimit())
                                                    printk(KERN_INFO "source
 route option
                                                                 %u.%u.%u.%u
 -> %u.%u.%u.%un",
                                                      NIPQUAD(iph->saddr), N
IPQUAD(iph->daddr));
                                                in_dev_put(in_dev);
                                                goto drop;
                                        }
                                        in_dev_put(in_dev);
                                }
                                if (ip_options_rcv_srr(skb))
                                        goto drop;
                        }
                }
 * 最后,调用目的路由的处理函数,如果这个IP包的目的地址不是本机器的地址,则调
用ip_forward处理,
   否则调用ip_local_deliever继续处理,为将IP包传送到上一层协议作准备。
                return skb->dst->input(skb);
        inhdr_error:
                IP_INC_STATS_BH(IpInHdrErrors);
        drop:
                kfree_skb(skb);
                return NET_RX_DROP;
        }
----------------------------------------------------------------------------
---------
int ip_local_deliver(struct sk_buff *skb)
实现过程:
 * 如果网络包是IP碎片的话,则调用ip_defrag进行碎片重组。
 * 检查调用netfilter的挂钩函数,完成以后调用ip_local_deliver_finish进行
  最后的IP层协议的处理。
        int ip_local_deliver(struct sk_buff *skb)
        {
                /*
                 *      Reassemble IP fragments.
                 */
                if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
                        skb = ip_defrag(skb);
                        if (!skb)
                                return 0;
                }
                return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,

                               ip_local_deliver_finish);
        }
----------------------------------------------------------------------------
---------
static inline int ip_local_deliver_finish(struct sk_buff *skb)
实现过程:
 * 根据IP头部的协议(skb->nh.iph->protocol)找到相应的协议定义pprot。
 * 如果有raw socket的话,调用raw_v4_input先处理。
 * 在通常情况下,一个哈希值只有一个协议相对应(inet_protos[hash]),则调用
  ipprot->handler(skb),从而将这个网路包送到上一层协议(例如icmp, udp, tcp
 等等)处理。
 * 否则的话调用ip_run_ipprot,找到相应的协议,再调用ipprot->handler(skb)处理

        static inline int ip_local_deliver_finish(struct sk_buff *skb)
        {
                int ihl = skb->nh.iph->ihl*4;
                /* Pull out additionl 8 bytes to save some space in protocol
s. */
                if (!pskb_may_pull(skb, ihl+8))
                        goto out;
                __skb_pull(skb, ihl);
                /* Point into the IP datagram, just past the header. */
                skb->h.raw = skb->data;
                {
                        /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE=
=MAX_INET_PROTOS */
                        int protocol = skb->nh.iph->protocol;
                        int hash = protocol & (MAX_INET_PROTOS - 1);
                        struct sock *raw_sk = raw_v4_htable[hash];
                        struct inet_protocol *ipprot;
                        int flag;
                        /* If there maybe a raw socket we must check - if no
t we
                         * don't care less
                         */
                        if(raw_sk != NULL)
                                raw_sk = raw_v4_input(skb, skb->nh.iph, hash
);
                        ipprot = (struct inet_protocol *) inet_protos[hash];

                        flag = 0;
                        if(ipprot != NULL) {
                                if(raw_sk == NULL &&
                                   ipprot->next == NULL &&
                                   ipprot->protocol == protocol) {
                                        int ret;
                                        /* Fast path... */
                                        ret = ipprot->handler(skb);
                                        return ret;
                                } else {
                                        flag = ip_run_ipprot(skb, skb->nh.ip
h, ipprot,
                                                                       (raw_
sk != NULL));
                                }
                        }
                        /* All protocols checked.
                         * If this packet was a broadcast, we may *not* repl
y to it,
                         * since that causes (proven, grin) ARP storms and a
 leakage
                         * of memory (i.e. all ICMP reply messages get queue
d up for
                         * transmission...)
                         */
                        if(raw_sk != NULL) {    /* Shift to last raw user */

                                raw_rcv(raw_sk, skb);
                                sock_put(raw_sk);
                        } else if (!flag) {             /* Free and report e
rrors */
                                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_
UNREACH, 0);
        out:
                                kfree_skb(skb);
                        }
                }
                return 0;
        }

--

    欲慰韶华携吴钩,剑雪刀霜遣风流。
    一蓑烟雨平生事,道无狂嚣亦无愁!

※ 来源:·UNIX编程 apue.dhs.org·[FROM: 166.111.160.6] --
※ 转寄:·UNIX编程 apue.dhs.org·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店