Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805232
  • 博文数量: 106
  • 博客积分: 1250
  • 博客等级: 少尉
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-09 09:38
文章分类

全部博文(106)

文章存档

2014年(1)

2013年(13)

2012年(92)

分类: LINUX

2013-01-27 14:42:32

原文地址:ip_route_input函数分析(2) 作者:shelcod

继续读ip_route_input_slow函数
/*
 *    NOTE. We drop all the packets that has local source
 *    addresses, because every properly looped back packet
 *    must have correct destination already attached by output routine.
 *    我们丢弃所有从127.0.0.1发来的包
 *   
 *    Such approach solves two big problems:
 *    1. Not simplex devices are handled properly.
 *       合理解决多设备的问题
 *    2. IP spoofing attempts are filtered with 100% of guarantee.
 *       百分百过滤IP spoofing尝试
 */

static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                   u8 tos, struct net_device *dev)
{
    struct fib_result res;
    struct in_device *in_dev = in_dev_get(dev);
    struct flowi fl = { .nl_u = { .ip4_u =
                      { .daddr = daddr,
                    .saddr = saddr,
                    .tos = tos,
                    .scope = RT_SCOPE_UNIVERSE,
                      } },
                .mark = skb->mark,
                .iif = dev->ifindex };
    unsigned    flags = 0;
    u32        itag = 0;
    struct rtable * rth;
    unsigned    hash;
    __be32        spec_dst;
    int        err = -EINVAL;
    int        free_res = 0;
    struct net    * net = dev_net(dev);

    /* IP on this device is disabled. */

    if (!in_dev)
        goto out;

    /* Check for the most weird martians, which can be not detected
       by fib_lookup.
     */

    if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) ||
        ipv4_is_loopback(saddr))
        goto martian_source;

    if (daddr == htonl(0xFFFFFFFF) || (saddr == 0 && daddr == 0))
        goto brd_input;

    /* Accept zero addresses only to limited broadcast;
     * I even do not know to fix it or not. Waiting for complains :-)
     */
    if (ipv4_is_zeronet(saddr))
        goto martian_source;

    if (ipv4_is_lbcast(daddr) || ipv4_is_zeronet(daddr) ||
        ipv4_is_loopback(daddr))
        goto martian_destination;

    /*
     *    前面都是通过检查包的源和目的地址的合理性来确定对不同包的处理
     *    Now we are ready to route packet.
     */
    if ((err = fib_lookup(net, &fl, &res)) != 0) { /*查找路由*/
        if (!IN_DEV_FORWARD(in_dev))  /*没找到,先判断转发标志是否打开*/
            goto e_hostunreach;
        goto no_route;
    }
    /*z找到路由的标志*/
    free_res = 1;

    RT_CACHE_STAT_INC(in_slow_tot);

    /*根据查找到的路由类型,分类处理*/
    if (res.type == RTN_BROADCAST)
        goto brd_input;

    if (res.type == RTN_LOCAL) {
        int result;
        /*如果是发给本机的包,则验证原地址是否合法*/
        result = fib_validate_source(saddr, daddr, tos,
                         net->loopback_dev->ifindex,
                         dev, &spec_dst, &itag, skb->mark);
        if (result < 0)
            goto martian_source;
        if (result)
            flags |= RTCF_DIRECTSRC;
        spec_dst = daddr;
        goto local_input;
    }

    if (!IN_DEV_FORWARD(in_dev))
        goto e_hostunreach;
    if (res.type != RTN_UNICAST)
        goto martian_destination;
 
    /*当查到的路由类型是指向远端的主机,把此路由加入cache中*/
    err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);
done:
    in_dev_put(in_dev);
    if (free_res)
        fib_res_put(&res);
out:    return err;

/*当目的地址是广播地址,或查到的路由类型是广播类型*/
brd_input:
    if (skb->protocol != htons(ETH_P_IP))
        goto e_inval;

    if (ipv4_is_zeronet(saddr))
        spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
    else {
        err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst,
                      &itag, skb->mark);
        if (err < 0)
            goto martian_source;
        if (err)
            flags |= RTCF_DIRECTSRC;
    }
    flags |= RTCF_BROADCAST;
    res.type = RTN_BROADCAST;
    RT_CACHE_STAT_INC(in_brd);

/*当查找到的路由指向本机时*/
local_input:
    /*分配缓存路由项空间,并以确定的spec_dest等信息给路由项赋值*/
    rth = dst_alloc(&ipv4_dst_ops);
    if (!rth)
        goto e_nobufs;

    rth->u.dst.output= ip_rt_bug;
    rth->u.dst.obsolete = -1;
    rth->rt_genid = rt_genid(net);

    atomic_set(&rth->u.dst.__refcnt, 1);
    rth->u.dst.flags= DST_HOST;
    if (IN_DEV_CONF_GET(in_dev, NOPOLICY))
        rth->u.dst.flags |= DST_NOPOLICY;
    rth->fl.fl4_dst    = daddr;
    rth->rt_dst    = daddr;
    rth->fl.fl4_tos    = tos;
    rth->fl.mark    = skb->mark;
    rth->fl.fl4_src    = saddr;
    rth->rt_src    = saddr;
#ifdef CONFIG_NET_CLS_ROUTE
    rth->u.dst.tclassid = itag;
#endif
    rth->rt_iif    =
    rth->fl.iif    = dev->ifindex;
    rth->u.dst.dev    = net->loopback_dev;
    dev_hold(rth->u.dst.dev);
    rth->idev    = in_dev_get(rth->u.dst.dev);
    rth->rt_gateway    = daddr;
    rth->rt_spec_dst= spec_dst;
    rth->u.dst.input= ip_local_deliver; /*路由查找结束后会调用此函数把报文送给上层处理*/
    rth->rt_flags     = flags|RTCF_LOCAL;
    if (res.type == RTN_UNREACHABLE) {
        rth->u.dst.input= ip_error;
        rth->u.dst.error= -err;
        rth->rt_flags     &= ~RTCF_LOCAL;
    }
    rth->rt_type    = res.type;
    hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
    err = rt_intern_hash(hash, rth, NULL, skb, fl.iif); /*想缓存中插入新的路由项*/
    goto done;

/*没有查找到路由的时候,向缓存中添加一条不可达路由项*/
no_route:
    RT_CACHE_STAT_INC(in_no_route);
    spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
    res.type = RTN_UNREACHABLE;
    if (err == -ESRCH)
        err = -ENETUNREACH;
    goto local_input;

    /*
     *    Do not cache martian addresses: they should be logged (RFC1812)
     */
martian_destination:
    RT_CACHE_STAT_INC(in_martian_dst);
#ifdef CONFIG_IP_ROUTE_VERBOSE
    if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
        printk(KERN_WARNING "martian destination %pI4 from %pI4, dev %s\n",
            &daddr, &saddr, dev->name);
#endif

e_hostunreach:
    err = -EHOSTUNREACH;
    goto done;

e_inval:
    err = -EINVAL;
    goto done;

e_nobufs:
    err = -ENOBUFS;
    goto done;

martian_source:
    ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
    goto e_inval;
}

以上函数都是从ip_route_input函数开始,所调用到的部分函数。当网卡收到报文到达IP层后,IP层先作路由查询以便决定把它送到哪。而根据源与目的地址以及其他方面的检测,路由分了多播路由,广播路由,单播路由,还有本地路由。这些路由在cache中的生存策略也是不同的。一个绝对重点的函数赋值就是rth->u.dst.input,最后函数在IP层的分发就是通过这个函数实现。
阅读(2342) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~