手把手教你搭建骨干网之:TCP专线 vs IP搬家
目前碰到一个需求:要搭建一个BGP的route view。
就像AT&T或者HE的那样:
telnet route-server.he.net
telnet route-server.ip.att.net
可以观测全网的BGP路由表
我们知道vulr支持BGP session,只要你有一个ASN以及一段不低于48位的v6地址,就可以申请BGP peer了,申请完了之后发现 ,vulr不仅提供提供v6的peer,也提供了v4的peer,而且把全网的路由都传了下来,这是一件令人兴奋的事情。
这也让我有了搭建一个route view的想法,但是理想是美好的,现实是残酷的,刚把v4的session配置好,机器就死机了,经过一秒钟的思考,就想到了原因:小鸡的配置太垃圾了,1C1G的配置还想拿80w的v4路由,这不是开玩笑呢。可是即使是这样垃圾的配置每月也要5$,都有些肉疼了。
工程师就是来解决问题的,尤其是“网红”,哦不对,是“网工”(认清自己的定位很重要)。
于是在晚上睡不着觉的时候,想了一个方案:既然BGP的session是基于tcp的,为啥不能将这个TCP的流量通过互联网搬到自己家里呢,家里随便一个小盒子的配置都可以接下80w的v4路由。
搭建一条 TCP “专线”
接下来可以想到的第一个问题就是如何把一个监听在vulr公网的TCP端口转移到本地的盒子上面呢,或者如何把本地盒子发起的TCP流量转移到vulr虚拟机的出口,我们知道BGP TCP的端口是179,BGP session两端中的任一端都可以主动发起连接请求,因此只要保证任意一端可以主动发起连接就可以成功。
这里我们做一个假设:
本地“盒子”:1.1.1.1
Vulr VM:2.2.2.2
Vulr 网关:169.254.169.254
方案一:由本地“盒子“主动发起连接
这种方案相对而言是比较简单的,而且也适合于本地盒子没有公网v4地址的场景。
我们只需在vulr vm上面执行一个命令:
socat -d TCP4-LISTEN:179,reuseaddr,forkTCP4: 169.254.169.254:179
这条命令的原理也很简单,就是在vulr vm上面将监听在179上面的端口转发到vulr 网关的179,在这里socat相当于一个tcp路由器的角色,将一个TCP连接路由到了另外一个TCP连接,当然socat也可以实现将一个TCP连接路由到一个UDP的连接。
聪明的小伙伴可能会提出说万能的iptables可以实现,通过DNAT功能,但是有一个问题需要注意就是,我们需要让vulr 网关看到session 会话的源地址是vulr vm的地址而不是本地盒子的地址,因为BGP的配置中显式的指明了两端的v4地址(这里假设本地盒子配置中的peer地址是vulr vm的地址),因此还需要配置一个SNAT,这种方法太丑陋了。
方案二:由vulr 网关主动发起连接
同样,我们也是只需在vulr vm上面执行一个命令:
socat -d TCP4-LISTEN:179,reuseaddr,forkTCP4: 1.1.1.1:179
但是这样的就要求本地盒子的v4地址是暴露在公网的,可以被动的接受连接
骨感的现实
配置完了后,show ip bgp sum 一看,感觉事情不妙,会话没有起来
抓包看一下吧
三次握手都已经OK了啊,为什么session还是没有起来呢?
再次检查配置发现,vulr网关的配置中默认开启了MD5的验证,而这个MD5的消息是放在TCP报文的Option中的,并且这个Hash中包含了TCP的伪头部信息,也是包含了session的两端IP地址信息,
这样就导致了两个问题:
- socat目前没有看到可以支持TCP的MD5认证
- 在上面的方案一中,本地盒子的BGP配置中peer地址是指向的vulr地址,而不是vulr网关的地址,即使socat支持了MD5,校验还是失败的。
好吧,要抛弃socat的方案了,只能选择iptables了,因为iptables只会改变TCP报文的三层源目地址信息,不会影响到TCP的option选项。
给IP搬个家
为了保证MD5的准确性,我们需要将两端BGP配置保持一致,即本地盒子的配置peer 指向vulr网关:169.254.169.254,而且盒子的源地址为vulr vm的地址2.2.2.2,这样才能保证两端的配置是一致的。
是不是有点凌乱了
这个方案,我也是思考了好久才梳理清楚
本质还是很简单的,即把vulr vm的地址2.2.2.2搬个家,将其搬到本地盒子上面,比如将其配置到lo interface上面
但是从经验来讲,这样一定会有一些问题,同一个IP地址正常不应该配置在两台机器上面,如果想做anycast对一些无状态的应用应该还好,可是对于TCP这样有状态的服务来说肯定是有问题的
怎么办呢?
还是需要整一些偏方
首先由于vulr vm的2.2.2.2这个地址是在vulr vm上面发布到公网的,因此需要在vulr vm 跟本地盒子之间跑一个tunnel或者叫overlay的网络,比如拿ppp over ssh 跑一个三层的点对点的网络,来让2.2.2.2这个地址可以直接在两个节点指点路由走私有的overlay网络
本地盒子tunnel ip:10.0.0.1
Vulr vm tunnel ip:10.0.0.2
然后在本地盒子上面将vulr 网关地址169.254.169.254指向下一跳10.0.0.2
这样基础的配置工作就完成的差不多了,我们通过分析TCP的建联过程分析一些还需要哪些工作没有完成
假设一:由vulr网关主动发起会话
我们知道vulr网关默认发起会话的目的地址是2.2.2.2这个vulr vm的地址,如何将这个流量转发到本地盒子呢,因为流量的第一跳就是vulr vm,而且vulr vm上面也有2.2.2.2这个地址,这就轮到iptables 出马了,没有什么需求是一条iptables 规则搞不定的,如果有那就再加一条。
vulr vm:iptables -tnat -A PREROUTING -d 2.2.2.2/32 -p tcp -m tcp --dport 179 -j DNAT--to-destination 10.0.0.1:179
本地盒子:iptables -t nat -A PREROUTING -d 10.0.0.1/32-p tcp -m tcp --dport 179 -j DNAT --to-destination 2.2.2.2:179
简单解释一下,先在vulr vm上面将流量的目的地址转换成本地盒子的tunnel地址,然后再在本地盒子上面将其转换成2.2.2.2地址
哎,转换的我好累
不过,在去frr里面看一下,是不是80w路由已经收入怀中了呢?幸运的话,路由表会刷新个几分钟的
如果不尽兴的话,可以再来看看另外一种方案
假设二:由本地盒子主动发起会话
由于本地盒子已经在lo interface上面配置了2.2.2.2这个地址,因此主动发起会话的时候,我们可以指定以2.2.2.2这个地址为源地址,并且也已经配置了vulr 网关169.254.169.254的路由指向vulr vm 的 tunnel地址,流量到达vulr vm后也应该会转发到vulr 网关,这样看起来是没什么问题
可是你有没有想过,流量是双向的,有去还要有回才可以
从vulr网关相应的回程流量到达vulrvm后,vulr vm会不会给你转发到盒子上面呢?
答案是否定的,因为这个回程流量的目的地址是vulr vm本身,肯定不会转发的
怎么办呢?
再来加两条iptables规则吧
本地盒子:iptables -t nat -A POSTROUTING -s 2.2.2.2/32 -o ppp0 -j MASQUERADE
Vulr vm:iptables -tnat -A POSTROUTING -s 10.0.0.1/32 -o eth0 -j MASQUERADE
功能跟上一种方案类似,只不过这里转变的是源地址
费了这么大的劲就为了看到几条路由而已