话说有些事情十分适合在放假前的一天折腾一天,但绝对不适合在你准备去吃饭前多看一眼...我上周就碰到了这么一件揪心的事,最终以低血糖收场,十分狼狈地四处觅食,却觅到了一包超级辣的鸡爪,吃完后感觉症状加重了,于是向邻座的同事乞讨了巧克力糖,唉...这一切起源于我在准备吃午饭前一头扎进了一个技术问题,本以为能靠配置搞定,最终却还是不得不以修改The Fxxxing code告终!事情起源于公司的一个禁令:禁止上外网!
禁止工作时间上外网,碰到问题就只能拖着,多么奇葩的行政管理策略。事实上,想在工作期间上外网一般有三个办法:
1.如果看直播之类的,可以躲在厕所里蹲着看,没人知道你在看什么,代价就是3G流量和麻醉状态的腿和脚;
2.填一张签报单,说明自己上网的理由,比如查资料之类很笼统但却没有漏洞的理由,然后找领导签字,一般都会批准的,然后你就可以在查资料之余顺便看看新闻,天气预报之类的;
3.原自公司网络管理的漏洞,仅仅封掉了熟知端口,有些端口却开着,如果你在家里开一台Linux机器,那么就可以通过家里的机器上网了。
以上3点中,第1点基本不可能了,因为自公司搬家以后,厕所就一点信号都没有了,别说上网,万一没带纸都是很麻烦的事,第二点大家都会做,也确实这么做了,结果公司禁令的最大效果就是各种打印,各种签字,各种跑,但是技术人员不满足于这种非技术的解决方式,于是一定要尝试一下第3种方式。
一般而言,家庭网络的拓扑都是在最外面有一个无线路由器,然后里面几个设备,PC,各种PAD,手机之类的,PC逐渐不再流行了,是守旧的思想在促使很多人一定要买或者组装高大上的PC,就像10年前很多人钟情于目前几乎已经绝迹的大型机一样。在这个后终端时代,买一块可以载有Linux的小板子也许是个不错的主意,它耗电少,无噪音,小型,随便塞在哪里都可以,长期开机也无所谓,它是做家庭上网代理的最好选择了。同事买了一块这样的小板子,据说很不错。现在想在公司的时候也能通过家里的那个小设备上网,需要做些什么呢?很显然,造一个IPIP或者GRE隧道是不错的选择,可是如果想加密的话,就必然要用某种VPN技术,那么首选的是OpenVPN,因为相比IPSec而言,我们有现成的配置。然而OpenVPN参数太多,也麻烦,于是想到了用simpletun,一个几乎仅仅是用来学习的,超级小的,根本称不上项目的小东西,虽然它没有加密功能,但是为它加入一个base64编码支持还是容易的...于是就选择用simpletun建立隧道。
simpletun本身一点问题都没有,很简单就把隧道建立了,家里的作为服务端,公司的作为客户端,因为公司防火墙只让主动出不让主动进。那么还有什么问题吗?问题来了。用tun模式还是tap模式呢?我比较倾向于tap模式,因为这样就可以把家里的局域网bridge到公司了,公司的机器和家里的小设备以及家里的路由器在一个网段,多好!但是这需要做两个额外的工作:
1.把家里小设备的物理网卡eth0和simpletun启动的虚拟网卡tap0用brctl命令做成一个网桥,然后让这个网桥接管原来eth0上的IP地址,
2.如果公司的机器连接家里的小设备成功,那么其simpletun的虚拟网卡地址也要配置成和家里局域网同一个网段的。
以上第2个问题很好解决,难的是第1个。要知道,对家里小设备的操作是远程SSH上去的,数据包通过家里路由器的公网IP连接,之后被路由器DNAT到小设备的eth0的IP地址,这就意味着在网桥设置期间,必须保证这个IP地址的连通性,但是目前的Linux Bridge机制是不能支持的。
通观上述过程,其实和虚拟网卡没有关系,于是可以把整个问题转化为:设eth0上有一个IP,为ip,如何找一个办法,使这个ip在下列过程中始终保持连通性,该过程为,将eth0加入一个新创建的网桥。整个问题中,关键的操作有两点,如下所示:
1.网桥起来的那一刻;
2.把eth0加入到网桥的那一刻;
有一点是毋庸置疑的,那就是只要你把eth0加入到网桥,如果eth0收到数据包,内核会认为该数据包是网桥接收的而不再是eth0接收的,包括ARP Reply在内,于是系统的arp表中本来拥有的路由器的arp项:
192.168.1.1 00:11:22:33:44:55 eth0
就会变成:
192.168.1.1 00:11:22:33:44:55 br0 或者 192.168.1.1 (incomplete) br0(如果br0还没有up的情况下)
要想数据通信能完成,路由结果项的邻居dev字段必须和arp表项的邻居dev字段一致,这也是二层,三层之间的一个一致性渠道。换句话说,要想保持IP的连通性,必须要在将eth0加入网桥的那一刻同时改变路由,而这是不可能的,因为brctl addif操作只完成一件事,即将eth0加入br0!
如果换一个思路,即先将路由生效,然后再加eth0到br0呢?同样的问题,路由项的dev改变为br0了,然而此时arp项的dev还是eth0!总之就是路由项的dev和arp项的dev在网桥的操作序列中总是会有一个操作导致其不一致,而这一刻的不一致将导致断网,后续的操作将无法完成,就算是一旦不一致了,只能靠另一个配置使其一致,它自己并不会收敛成一致状态。
其本质原因有两点:1.brctl以及route操作都是原子的,二者互不牵连;2.网桥没有一个中间过渡状态。解决这个问题就是要么将brctl和route关联起来,要么引入一个中间状态。想想就知道,关联brctl和route肯定是不好的,毕竟我们这个需求不是一个普遍的需求,也有很多办法可以解决它,比如设置一个开机if-up脚本,或者写一个批处理后台执行。之所以这么较真儿非要来个online作业是因为我一直都以为自己能解决所有的网络问题...于是就决定用第二种方式,引入一个中间状态,将多个互不相关的触发机制合并为单一的触发,比如虽然调用了addif将eth0加入了br0,调用ifconfig为br0设置了IP地址,但是在那个单一的触发动作没有执行前,一切都不生效。显而易见的做法是,如果br0没有up,则即使eth0加入了br0还依然使用eth0进行数据通信。找到要修改的代码很容易,因为Linux网桥的代码本身就很容易,修改net/bridge/br_input.c的br_handle_frame函数:
struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) { const unsigned char *dest = eth_hdr(skb)->h_dest; int (*rhook)(struct sk_buff *skb); ////////加入以下代码 int flags = p->br->dev->flags; if (!(flags & IFF_UP)) { return skb; } //////// ..... }然后加载修改后的模块后执行以下序列:
一个关于Linux Bridge配置的吐嘈,布布扣,bubuko.com
原文地址:http://dog250.blog.51cto.com/2466061/1412926