OpenWRT嵌入式Linux故障排除一例

少年恃险若平地,独倚长剑凌清秋。这篇文章主要讲述OpenWRT嵌入式Linux故障排除一例相关的知识,希望能为你提供帮助。
跟大数据没关系,只是帮朋友忙排了个错记录一下。


以前关系很不错的同事,目前在企业级wifi领域创业,采购了我们的大数据服务,正在给他做平台的搭建和调试。然后这几天他这个CEO在调试路由器的时候遇到一些问题,在搞大数据的同时捎带手解决了一下他这个问题。


OpenWRT,嵌入式Linux,主要用在MIPS或ARM设备上。路由器和wifi设备很多会采用这个系统,特点是轻巧。


Coova-Chilli,在openwrt下的接入访问控制器,提供认证网关,可以使用radius或http来做接入计费等工作。


正常的话,在启动chilli以后,会启动四个tun虚拟隧道网卡,而故障是偶发性的,不定期的会有两个IP地址一样的tun设备。比如是这样

tun0 10.1.0.1

tun1 10.1.0.1
tun2 10.2.0.1
tun3 10.3.0.1
tun4 10.4.0.1
正常的情况下是应该只有tun0-3的设备,但是每次启动都会多出一两个tun,而且还不固定,有时候是tun0-1 IP地址一样,有时候tun2-3 IP地址一样。而且OpenWRT默认是不记录syslog的。很难排查。其实可以从logread里面读取syslog,但是syslog里其实没记录任何东西。


那哥们以前也是写代码的,苦熬了三个通宵没找到问题在哪,在chilli启动脚本里面设置了各种记log,wait,sleep,都没用。下午过去讨论完当前大数据平台的需求就没事了,然后我闲的蛋疼就给他看了一下那个脚本,chilli脚本应该没有太多的问题,然后他是按照官方部署文档搭建的。一开始也没看出问题在哪。chilli脚本默认是放在/etc/init.d目录下的。按说不会有问题,后来快感来了,他告诉我他写了一个命令在rc.local做启动,我看了一下rc.local里面,他写了一个启动脚本放到了/root下面。vi 那个在/root下的启动脚本,里面写了一个/etc/init.d/chilli restart。我就问他这是干嘛用的,他说wrt官方让这样写,说这样写保险。我尝试注销掉restart行,重启10遍,tun隧道都毫无问题。20分钟搞定。
【OpenWRT嵌入式Linux故障排除一例】

问题分析


chilli原始脚本如下
#!  /bin/sh        ###  BEGIN  INIT  INFO        #  Provides:                    chilli        #  Required-Start:        $remote_fs  $syslog  $network        #  Required-Stop:          $remote_fs  $syslog  $network        #  Default-Start:          2  3  4  5        #  Default-Stop:            0  1  6        #  Short-Description:  Start  CoovaChilli  daemon  at  boot  time        #  Description:              Enable  CoovaChilli  service  provided  by  daemon.        ###  END  INIT  INFO        PATH=/sbin:/bin:/usr/sbin:/usr/bin        DAEMON=/usr/sbin/chilli        NAME=chilli        DESC=chilli        START_CHILLI=0        if  [  -f  /etc/default/chilli  ]  ;   then        .  /etc/default/chilli        fi        if  [  "$START_CHILLI"  !=  "1"  ]  ;   then        echo  "Chilli  default  off.  Look  at  /etc/default/chilli"        exit  0        fi        test  -f  $DAEMON  ||  exit  0        .  /etc/chilli/functions        MULTI=$(ls  /etc/chilli/*/chilli.conf  2> /dev/null)        [  -z  "$DHCPIF"  ]  & &   [  -n  "$MULTI"  ]  & &   {        for  c  in  $MULTI;           do        echo  "Found  configuration  $c"        DHCPIF=$(basename  $(echo  $c|sed  \'s#/chilli.conf##\'))        export  DHCPIF        echo  "Running  DHCPIF=$DHCPIF  $0  $*"        sh  $0  $*        done        exit        }        if  [  -n  "$DHCPIF"  ];   then        CONFIG=/etc/chilli/$DHCPIF/chilli.conf        else        CONFIG=/etc/chilli.conf        fi        [  -f  $CONFIG  ]  ||  {        echo  "$CONFIG  Not  found"        exit  0        }        check_required        RETVAL=0        prog="chilli"        case  "$1"  in        start)        echo  -n  "Starting  $DESC:  "        /sbin/modprobe  tun  > /dev/null  2> & 1        echo  1  >   /proc/sys/net/ipv4/ip_forward        writeconfig        radiusconfig        test  ${HS_ADMINTERVAL:-0}  -gt  0  & &   {         (crontab  -l  2> & -  |  grep  -v  $0        echo  "*/$HS_ADMINTERVAL  *  *  *  *  $0  radconfig"        )  |  crontab  -  2> & -        }        ifconfig  $HS_LANIF  0.0.0.0        start-stop-daemon  --start  --quiet  --pidfile  /var/run/$NAME.$HS_LANIF.pid  \\        --exec  $DAEMON  --  -c  $CONFIG        RETVAL=$?        echo  "$NAME."        ; ;         checkrunning)        check=`start-stop-daemon  --start  --exec  $DAEMON  --test`        if  [  x"$check"  !=  x"$DAEMON  already  running."  ]  ;   then        $0  start        fi        ; ;         radconfig)        [  -e  $MAIN_CONF  ]  ||  writeconfig        radiusconfig        ; ;         restart)        $0  stop        sleep  1        $0  start        RETVAL=$?        ; ;         stop)        echo  -n  "Stopping  $DESC:  "        crontab  -l  2> & -  |  grep  -v  $0  |  crontab  -        start-stop-daemon  --oknodo  --stop  --quiet  --pidfile  /var/run/$NAME.$HS_LANIF.pid  \\        --exec  $DAEMON        echo  "$NAME."        ; ;         reload)        echo  "Reloading  $DESC."        start-stop-daemon  --stop  --signal  1  --quiet  --pidfile  \\        /var/run/$NAME.$HS_LANIF.pid  --exec  $DAEMON        ; ;         condrestart)        check=`start-stop-daemon  --start  --exec  $DAEMON  --test`        if  [  x"$check"  !=  x"$DAEMON  already  running."  ]  ;   then        $0  restart        RETVAL=$?        fi        ; ;         status)        status  chilli        RETVAL=$?        ; ;         *)        N=/etc/init.d/$NAME        echo  "Usage:  $N  {start|stop|restart|condrestart|status|reload|radconfig}"  > & 2        exit  1        ; ;         esac        exit  0



问题在于,他在调试的时候,在for c in $MULTI循环里面,为了保证每个子进程都启动成功,加了一个wait,后面在建立tun通道的时候为了调试又加了几个sleep。照着官方文档,他又加了个restart到rc.local里面,这样问题就来了,/etc/init.d里面是自动执行chilli start命令的,而加上了wait和sleep。init.d的启动脚本会等待,而这时候Linux在不同的tty又启动了rc.local里面的chilli restart命令,于是两个或三个相同的tun IP地址就会共同存在。


反正问题解决了,鉴于他为这种破事熬了三个通宵,我就可以以先知的口吻教育这个亲自调试程序的CEO:“尽信书不如无书”。开源系统的官方文档往往滞后,可能新版本早就解决了需要restart的问题,但是文档没有及时更新,导致这种问题的发生。


总结,了解各种系统的工作原理是多么重要。


最后,帮这哥们发个招聘广告,他们表面上是个做wifi硬件的创业团队,气氛融洽,待遇优厚,其实是个气氛融洽,待遇优厚的大数据公司。公司的核心重点是基于hadoop的大数据挖掘和机器学习,欢迎网友自荐或推荐相关人才,不在于说你技术有多牛,就算刚从大学出来也没问题,我这老板朋友更看重你是否具备钻研和学习的精神,加入该公司会得到本屌的Hadoop开发与运维技术的亲身指导。机会难得,踊跃报名。

    推荐阅读