解决IPV6路由表删除时遇到的:No Such Process问题

  Busybox: 解决IPV6路由表删除时遇到的:No Such Process问题 收藏
环境:

    嵌入式系统,Busybox-1.00版本,IPV6网络环境,DHCPV6动态获取网关和路由信息。

问题:

    当使用route命令尝试删除默认网关:route -A inet6 del ::/0 gw 2001::1 dev br0

    操作失败,系统温馨提示: SIOC[ADD|DEL]RT: No Such Process

分析:

    Busybox-1.00是enbale了IPV6的支持选项的。同时从DHCP Server处获取的默认网关也是正确的。使用 route -A inet6 命令获取默认网关的相关信息如下:   


--------------------------------------------------------------------------------

Destination  Next Hop  Flag  Metric  Ref  Use  Iface

2001::1/64  ::/0   U  1024  0  0  br0


--------------------------------------------------------------------------------

    从Busybox-1.00的源码入手分析route del命令。route命令对应的源文件位于: busybox-1.00/networking/route.c位置处。从代码中查找"SIOC[ADD|DEL]RT: No Such Process"错误字符串,未果。发现在代码的440行,有一个函数调用可能比较相关:


--------------------------------------------------------------------------------

00439: if(ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0){

00440:      bb_perror_msg_and_die("SIOC[ADD|DEL]RT");

00441: }


--------------------------------------------------------------------------------

    从该段代码中可以看出,No Such Process的错误应该是由ioctl返回的ErrorCode映射生成的。而ioctl系统调用是实现了busybox和kernel通信的一个接口。由于我们使用的是del命令,所以传给kernel的命令应该是SIOCDELRT。内核为什么在处理这个命令的时候返回No Such Process错误呢? Linux的最大好处就是,如果你想搞清楚一个问题,你总是能够搞清楚它的原因。因为Kernel也是开放源码的。接下来,我们就进Kernel看看,SIOCDELRT是如何被处理的。

      Kernel中处理IPV6路由表设置的主要的代码位于:net/ipv6/route.c中。可以很方便找到处理SIOCDELRT命令的函数:ip6_route_del()。以下是ip6_route_del()函数的大体流程。

     
+ expand sourceview plaincopy to clipboardprint?
static int ip6_route_del(struct fib6_config *cfg)  
{  
    struct fib6_table *table;  
    struct fib6_node *fn;  
    struct rt6_info *rt;  
    int err = -ESRCH;  
    table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);  
    if (table == NULL)  
        return err;  
    read_lock_bh(&table->tb6_lock);  
    fn = fib6_locate(&table->tb6_root,  
             &cfg->fc_dst, cfg->fc_dst_len,  
             &cfg->fc_src, cfg->fc_src_len);  
    if (fn) {  
        for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {  
            if (cfg->fc_ifindex &&  
                (rt->rt6i_dev == NULL ||  
                 rt->rt6i_dev->ifindex != cfg->fc_ifindex))  
                continue;  
            if (cfg->fc_flags & RTF_GATEWAY &&  
                !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))  
                continue;  
            if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)  
                continue;  
            dst_hold(&rt->u.dst);  
            read_unlock_bh(&table->tb6_lock);  
            return __ip6_del_rt(rt, &cfg->fc_nlinfo);  
        }  
    }  
    read_unlock_bh(&table->tb6_lock);  
    return err;  

static int ip6_route_del(struct fib6_config *cfg)
{
 struct fib6_table *table;
 struct fib6_node *fn;
 struct rt6_info *rt;
 int err = -ESRCH;
 table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table);
 if (table == NULL)
  return err;
 read_lock_bh(&table->tb6_lock);
 fn = fib6_locate(&table->tb6_root,
    &cfg->fc_dst, cfg->fc_dst_len,
    &cfg->fc_src, cfg->fc_src_len);
 if (fn) {
  for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
   if (cfg->fc_ifindex &&
       (rt->rt6i_dev == NULL ||
        rt->rt6i_dev->ifindex != cfg->fc_ifindex))
    continue;
   if (cfg->fc_flags & RTF_GATEWAY &&
       !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))
    continue;
   if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
    continue;
   dst_hold(&rt->u.dst);
   read_unlock_bh(&table->tb6_lock);
   return __ip6_del_rt(rt, &cfg->fc_nlinfo);
  }
 }
 read_unlock_bh(&table->tb6_lock);
 return err;
}
 

   我们首先看到返回值err被初始化成:-ESRCH。这是什么一个宏呢?在include/asm-generic/error-base.h文件中定义如下:


--------------------------------------------------------------------------------

   #define ESRCH 3 /* No such process */


--------------------------------------------------------------------------------

   多么熟悉的一个错误啊。:) 所以我们了解到原来No such process在kernel中就是被定义成ESRCH宏的。从字面上的意思理解,应该是在路由表中没有找到相应的item。是在哪里出错返回的呢?从代码中看,可能的返回值是-ESRCH以及__ip6_del_rt()函数的返回值。而__ip6_del_rt()函数是不会返回No such process错误的。所以我们根本没有进入到__ip6_del_rt()调用。经过分析,有以下4个point可能会导致该函数提前返回。

Point1: 返回的fn是NULL。


--------------------------------------------------------------------------------

  fn = fib6_locate(&table->tb6_root,&cfg->fc_dst, cfg->fc_dst_len,&cfg->fc_src, cfg->fc_src_len);


--------------------------------------------------------------------------------

Point2-Point4:


--------------------------------------------------------------------------------

   if (fn) {

       for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {

          if (cfg->fc_ifindex &&   (rt->rt6i_dev == NULL ||    rt->rt6i_dev->ifindex != cfg->fc_ifindex))

               continue; // Point 2

          if (cfg->fc_flags & RTF_GATEWAY && !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway))

               continue; // Point 3

          if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)

               continue; // Point 4

          ... ...

          return __ip6_del_rt(rt, &cfg->fc_nlinfo);

          }

   }


--------------------------------------------------------------------------------

    最后通过在kernel中插入debug信息,发现为题出现在Point4。原因在于:cfg->fc_metric == 1 而 rt->rt6i_metric == 1024。问题很明确了busybox传进来的metric和内核从路由表中查到的metric不一致。根据我们route -A inet6命令查询出来的结果来看,正确的metric应该是1024,意味着busybox传错了参数。回到busybox中看看为什么metric传的是1?还是busybox-1.00/networking/route.c文件,相关代码如下:

view plaincopy to clipboardprint?
01.static void INET6_setroute(int action, char **args)  
02.{  
03.    struct sockaddr_in6 sa6;  
04.    struct in6_rtmsg rt;  
05.    int prefix_len, skfd;  
06.    const char *devname;  
07.    assert((action == RTACTION_ADD) || (action == RTACTION_DEL));  
08.    {  
09.        /* We know args isn't NULL from the check in route_main. */ 
10.        const char *target = *args++;  
11.        if (strcmp(target, "default") == 0) {  
12.            prefix_len = 0;  
13.            memset(&sa6, 0, sizeof(sa6));  
14.        } else {  
15.            char *cp;  
16.            if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */ 
17.                *cp = 0;  
18.                prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);  
19.            } else {  
20.                prefix_len = 128;  
21.            }  
22.            if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {  
23.                bb_error_msg_and_die("resolving %s", target);  
24.            }  
25.        }  
26.    }  
27.    /* Clean out the RTREQ structure. */ 
28.    memset((char *) &rt, 0, sizeof(struct in6_rtmsg));  
29.    memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));  
30.    /* Fill in the other fields. */ 
31.    rt.rtmsg_dst_len = prefix_len;  
32.    rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);  
33.    rt.rtmsg_metric = 1;  
34.        ...  
35.} 
static void INET6_setroute(int action, char **args)
{
 struct sockaddr_in6 sa6;
 struct in6_rtmsg rt;
 int prefix_len, skfd;
 const char *devname;
 assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
 {
  /* We know args isn't NULL from the check in route_main. */
  const char *target = *args++;
  if (strcmp(target, "default") == 0) {
   prefix_len = 0;
   memset(&sa6, 0, sizeof(sa6));
  } else {
   char *cp;
   if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
    *cp = 0;
    prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);
   } else {
    prefix_len = 128;
   }
   if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
    bb_error_msg_and_die("resolving %s", target);
   }
  }
 }
 /* Clean out the RTREQ structure. */
 memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
 memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
 /* Fill in the other fields. */
 rt.rtmsg_dst_len = prefix_len;
 rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
 rt.rtmsg_metric = 1;
        ...
}

最后一行已经明确了busybox的错误!!

针对这一问题,如何修改呢?其实很简单,我们只需要把rt.rtmsg_metric = 1;改成rt.rtmsg_metric = 0;即可。当metric被设置成0的时候,就是告诉内核忽略该项参数的检查。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cometofly/archive/2010/12/09/6066251.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值