一次路径引发的iptables事故

背景:原来只是一个谎言

感悟:可恶的 /(根路径符号) ,浪费我三天时间!

特点:菜鸟级的时间拉锯战

引言:最近在嵌入式设备上做一个基于Linux的网关防火墙的项目,自然,iptables是必不可少的。但是,在最近的测试中因为粗心大意造成的一处错误却带来了不小的麻烦,详细说来就是。。。

 

时间:9 27

关键词:发现问题

因为之前一直没有测试过iptables的功能,只是交叉编译通过并能在板子上跑起来,输入ip + tab键也能找到这个命令,+ --help选项也能打印帮助信息,因此就以为这块没有问题,后续只要添加规则就可以了。

之前就将iptables的手册看了一遍,感觉也挺简单的,没什么东西,常用的规则就那么几个,至于其他繁杂的,用时再看也不迟。这天终于决定要大显身手一把了。

添加一条规则:

Iptables –t nat –A PREROUTING –p tcp –d 192.168.1.123 –dport 80 –j DNAT –to-destination 192.168.2.123:80

结果提示 –dport是 unkown option

奇怪了,怎么可能啊,在Linux主机上试了下这条命令,没问题,说明是支持dport这个选项的。不是有帮助吗,看看,虽然是e文,看起来有点不顺眼,但是莫办法呀,谁叫这东西是老外开发的呢!

粗略扫了一遍,发现的确没有—dport这个选项的说明,不过手册示例中到处都在用这个选项,而且在主机上也试过了,没问题的,不解中。。。

通常这种情况下,Google是不错的选择,于是开始就在网上瞎找。但是,就像那首歌唱的:“我是一只小小小小虫,想要搜呀搜,却怎么搜都搜不到。。。”。

在此绝望之际,我决定看看源代码,这不是开源的吗!还好,没深入多少就发现了这么一段话:

/* some explanations (after four different bugs

 * in 3 different releases): If we encounter a

 * parameter, that has not been parsed yet,

 * it's not an option of an explicitly loaded

 * match or a target.  However, we support

 * implicit loading of the protocol match

 * extension.  '-p tcp' means 'l4 proto 6' and

 * at the same time 'load tcp protocol match on

 * demand if we specify --dport'.

 *

 * To make this work, we need to make sure:

 * - the parameter has not been parsed by

 *   a match (m above)

 * - a protocol has been specified

 * - the protocol extension has not been

 *   loaded yet, or is loaded and unused

 *   [think of iptables-restore!]

 * - the protocol extension can be successively

 *   loaded

 */

大概意思是说,--dport选项与传输层及其协议相关,需要装载相关的模块才能使用。这倒也是,ip层没有端口一说嘛,既然这样,那就装载相关的模块呗。问题又来了,规则里不是指定了协议是tcp吗,按道理tcp模块就应该自动装载的啊,虽然我们的各级government有很多的按道理,就像南京的法官说的,按常理XX那样做是没有道理的;但是,程序不一样,它可没有那么丰富的想象力,所有的都是按照预定的规则来的,所以我还是老老实实的加了个 –m tcp再尝试。结果仍然不行,看来问题也不再这里。既然这样,那么我就换个规则试试,不用你—dport不行吗!哼!来个新的规则:

Iptables –I INPUT –p icmp –j DROP

丢到所有的ICMP报文,这够简单的了吧!回车,好像不行,出现了新的打印:

Couldn’t load target libipt_standard.so:usr/lib/libexec/xtables/libipt_standard.so找不到或者不存在。这样来看,之前的规则无法识别—dport就是因为tcp模块没有加载成功。另外,从这条打印看出,程序是到usr/lib/libexec/xxx这个路径下去找共享动态库的,那么看看这个路径下有没有这个库不就完了?

不看不知道,一看吓一跳,别说库,连路径都不存在。那么文件系统中到底有没有这个库呢?搜搜看。结果发现在/usr/lib下找到iptables使用的动态库了。如此看来是库存放的路径不对了,或者是编译时指定的路径不对。既如此,解决这个问题要么创建对应的路径,然后将库拷贝过来;要么为iptables指定新的路径,就让其从/usr/lib下去找需要的库。这里选择后者尝试。

程序加载库的路径一般都是通过Makefile指定的,再配合环境变量完成最终的查找工作。通常情况下,软件的Makefile中都会指定程序加载库的默认前缀,就比如这里的libexec/xtables/,这相对环境变量中的路径来说实际上应该是后缀。所以要修改加载动态库的路径就可以通过修改Makefile来完成。又因为Makefile都是根据配置文件自动生成的,所以这步操作又可以放到软件的configure过程来完成。针对iptables,可以在configure时指定—with-xtables=usr/lib

Clean后重新编译,执行时发现查找的路径的确是configure中设置的了,但是动态库的加载还是不成功!郁闷!今天就到这里吧,明天接着战斗!要像韩剧中常说的,fighting!(现在发现游戏风云中这个词也常被说。)

 

时间:9  28

关键词:进一步发现问题

早上针对昨天最后的现象又测了测,整个过程可谓一波三折,有几次竟然发现能用了(每一次都着实兴奋了一阵),但不知为什么,测着测着就又不能用了,关键是没找到其中的规律。不知道是什么操作影响了iptables模块,或者是把什么地方改错了?但是每次的错误都是相同的,就是提示找不到库或者路径。

X,竟然对明明存在的库视而不见!

难道是库名不对,因为提示找不到的是libipt_xx.so,而实际目录下存在的是libxt_xx.so。但是转念一想,不会啊,早上测试时又成功过。 之前虽然也曾怀疑过库名的问题,但是后来网上搜索看到以前版本都是libipt_xxx.so,现在版本变为了libxt_xxx.so,也就不再注意这个问题,莫非真是库名的原因?

每当在这种无路可走的时候就想看看代码,每当想看代码的时候就给自己一个否定的理由:代码太多了,加之iptable代码以前从来没看过,还是算了吧。

于是开始上网搜,找了一大堆,看了一些,也有一些没看,不过大多都是不相干的。

一番搜索下来,发现一无所获,想想这不是漫无目的的瞎找嘛,还是歇歇吧,看看别的东西也好啊,换换脑子。比如最近又出了什么新的电子产品啊,什么日本右翼份子向中国大使馆仍烟筒啊,什么嫦娥二号要发射了,什么。。。。

看的脑子有点晕,本来是想放松的,算了。既然看代码有点畏惧,网搜有没有斩获,那就看看书吧,正好手边有一本 Linux 安全体系分析,里面也正好有一章是讲防火墙的,说不定会有什么发现呢。

走马观花过了一遍,整个内容大体是对netfilter和iptable的架构进行一些分析,但是没找到几张一目了然的图,都是粘代码,好点的再加点注释,但是看着还是烦,没心情仔细去看。再看看时间,发现都已经下班了,哎时间就这样浪费了。还是去吃饭正经,按照以往经验,说不定会有“众里寻他千百度,那人却在灯火阑珊处”的效果:因为灵感突发,就想到问题的原因了呢。

吃完饭,听听歌,满江的“最美的时光”,感觉还是蛮好听的。再杀一把,呵呵,就是传说中的三国杀。来个3v3吧,少不了再相互对骂一番。

一场酣畅淋漓的对杀下来,发现时间又不早了,该要回去了,可是有点不甘心啊,问题还么解决呢。为了补偿一下自己,干脆把书拿回去吧,说不定睡觉前还可以在稍微细的过一下,也说不定就可以从中发现什么了呢(至于要发现什么,也不知道,人在没有头绪的时候总会有一些奇怪的想法。。。)。于是背着笨笨的书,怀揣着美好的 “愿望” ,就这样回家了。

 

时间:9 29

关键词:解决问题

接着昨日继续。话说带着书回去,主要是为了安慰自己不安的心,睡觉前又过了一遍,就差做梦没有梦到了。

早上坐到电脑前,想想既然网上找不到,查书也没头绪,那就只能祭出杀手锏了:直接看代码!不过做这样的决定还是需要下点勇气的,吼吼!

说看就看,从那条打印作为切入点,看看是从哪儿出来的。结合之前看书获取的浅浅印象加上网上一番搜刮,还是找到那条让人头都大了的打印:

#ifndef NO_SHARED_LIBS

static void *load_extension(const char *search_path, const char *prefix, const char *name, bool is_target)

{

const char *dir = search_path, *next;

void *ptr = NULL;

struct stat sb;

char path[256];

 

do {

next = strchr(dir, ':');

if (next == NULL)

next = dir + strlen(dir);

snprintf(path, sizeof(path), "%.*s/libxt_%s.so",

         (unsigned int)(next - dir), dir, name);

 

if (dlopen(path, RTLD_NOW) != NULL) {

/* Found library.  If it didn't register itself,

   maybe they specified target as match. */

if (is_target)

ptr = find_target(name, DONT_LOAD);

else

ptr = find_match(name, DONT_LOAD, NULL);

} else if (stat(path, &sb) == 0) {

fprintf(stderr, "%s: %s\n", path, dlerror());

}

if (ptr != NULL)

return ptr;

snprintf(path, sizeof(path), "%.*s/%s%s.so",

         (unsigned int)(next - dir), dir, prefix, name);

if (dlopen(path, RTLD_NOW) != NULL) {

if (is_target)

ptr = find_target(name, DONT_LOAD);

else

ptr = find_match(name, DONT_LOAD, NULL);

} else if (stat(path, &sb) == 0) {

fprintf(stderr, "%s: %s\n", path, dlerror());

}

if (ptr != NULL)

return ptr;

dir = next + 1;

} while (*next != '\0');

 

return NULL;

}

#endif

就在xtables.c文件的这个函数中,好,就从这里入手了。

在之前之后加上点打印,看看到底走了什么逻辑。

从结果来看,可以肯定库名是没有问题的,基本逻辑是:软件是先加载libxt_xxx.so,如果加载不成功或者找不到就再加载libipt_xxx.so,这样就保证不会出问题,不论是在高版本环境还是低版本环境。再进一步跟踪,发现动态库本身就没有加载成功,但是库是存在的,并且找到位置和名称也没错,残酷的现实让我更加郁闷了,还好到了吃饭时间,先去吃饭吧。

本人吃饭不讲究味道,讲究速度,三下五除二,赶快吃完上来还可以上上网。

上网似乎只是习惯动作。对于像我这样的非主流网虫,正统是不敢兴趣的,旁门左道或可一览:比如,陕西师大为节约水资源,提供女性站立式小便池。看到这条信息,也着实吃了一惊,等细看完,不得不佩服现代人的想象力。具体细节这里就不便透露了,感兴趣的读者可以上网查看。再比如,因为之前看到关于电影山楂树之恋的报道,才知道这部小说挺流行的。最近也常会抽时间看看。今天恰好看到16节中老三的一句话,影响特别深:我父亲总是感叹,说毛泽东的那句话有道理:‘胜利往往来自于再坚持一下之后’。有时候,好像已经走到了绝境,以为再也没有希望了,但是如果再坚持一下,再坚持一下,往往就看到了胜利的曙光。恩,很有道理的一段话,同时下定决心要下午争取把问题解决。

好,回到正题。之前的测试都是登陆到系统后执行的,但是重启系统后发现一个现象,就是登陆之前命令执行都是好的,登陆后就提示找不到库了,因为登陆后环境变量同之前是不一样的,难道是因为环境变量被修改了的?有想法了就得赶紧实验。

几轮测试下来,发现只有环境变量中的根路径能产生影响,对比修改其他项都不管用,但是只要修改根路径为 / 后就一切正常了。

问题初步原因看来是找到了,太激动了!

激动归激动,但是问题根本原因还没有找到,

趁热打铁,追寻问题本质原因。既然修改根路径就好了,说明库本身是没有问题的,原因应该还是找不到库,又因为库倒确确实实是在/usr/lib/下的,那么最可能的原因就是iptables查找库的路径还是不对的,难道是找不到/usr/lib?正当盯着系统打印发呆时,突然感觉眼前一亮:找不到usr/lib/libipt_xx.so共享库,不是/usr/lib/libipt_xxx.so,我晕,少了一个根路径符号 / !!!难道是我在configure指定路径时用的是相对路径?打开配置文件一看,果然是!在想想,之前遇到的种种怪问题就都有了合理的解释,包括有几次能用的情况,那完全是个巧合,肯定是因为在根路径下操作的,这也正说明了为什么只有偶然的几次是可以的。我擦擦擦,要抓狂了。。。

冷静一下,重新修改了配置,在编译一遍跑起来,全好了!

浪费我这么多时间,完全就是因为一开始的一个失误啊。。。。

是不是该狠狠捶自己一下呢?呵呵,想想还是算了吧。虽然是个失误,但是在解决问题的过程中还是学到了不少东西。

我想以后再出现库找不到的问题,应该就能够很快的定位了。^_^

猜你喜欢

转载自blog.csdn.net/wwwyue1985/article/details/112438943