使用Linux作为MPLS路由器

2017年发生了两件大事

Linux终于获得了能用的原生的MPLSL3VPNVRF支持不过三年以后MPLS配置的完整文档尚付阙如近日我经过研究和阅读各种零散的资料成功在测试环境中配置了一个标准的MPLS核心网络架构因此写一篇文章来分享配置过程以及路上遇到的各种坑

MPLS

只玩IP路由不怎么接触大型运营商网络的同学们可能会对MPLS这个技术有些陌生如果要打一个简单的比方MPLSL3VPN就像是三层意义上的VLAN Trunking路由器上把三层接口加入VRF对应交换机上的VLAN每个路由器上可以有多个虚拟的路由表对应交换机上的ARP核心路由器之间传送的数据报文前面会被加上MPLS标签对应交换机上的VLAN标签保证多个路由表内的数据互不干扰MPLS的好处有很多

  • 用同一组路由器为不同的客户提供不同种类的服务
  • 允许不同的客户网使用相同的IP地址段
  • 核心节点不需要查找路由表只需要做简单的MPLS标签替换工作增加了包转发性能
  • 核心节点不需要保存客户的全量路由表只需要负责转发节约内存
  • 可以实现在客户无感知的情况下把多地的同一客户设备连接起来不同客户互相隔离
  • 简化复杂的三层配置
  • 可以设置特定流量走特定路径方便负载均衡和冗余

这么好的协议当然会有一些前提要求MPLS是一个2.5层协议也就是说它需要运行在二层之上所以如果要启用MPLS首先整个核心网路由器之间需要用支持二层的方式例如Ethernet连接或者使用对MPLS做了特殊支持的非二层隧道RFC2547bis例如GREL2TPMPLS的数据报文有8字节的头部因此你可能需要适当增大核心网的MTU

MPLS核心网的路由器一般分为两类PProvider即不连接任何客户设备的路由器以及PEProvider Edge即连接客户设备的路由器直接连接到PE的客户设备称为CECustomer Edge其它理论知识在此就不细讲了简单来说MPLS的配置需要以下几步

  1. 给每台PPE设置loopback端口的固定IP
  2. 通过IGP让所有PPE之间loopback IP都可以互相ping
  3. 在每台PPE上启动MPLS处理功能和LDP服务
  4. PE上配置iBGP启动MP-BGP功能
  5. PE上配置VRF并且将连接客户的端口加入相应的VRF
  6. 把客户的路由重分发到核心网的VRF路由表

接下来我用一个简化版的实验室环境来演示一下LinuxMPLS的实际配置过程

MPLS

要解决的问题

假设我们是一个跨城市的小ISP提供一种把客户在两个城市之间的站点连接起来的业务业务的实现方式很简单客户在两个城市分别拉一根网线到我们在同一城市的路由器配置好IP然后客户在两个城市的站点就魔法般互相接通了有一天突然来了两个客户——暂且称之为customer1customer2好了——都要购买我们的服务巧的是他们内网用的IP段是一模一样的如果我们的核心网络使用传统IP技术互联的话两个客户的内网可能就互通了并且还会互相打架但是如果我们的核心网络使用了MPLS技术这个配置不费吹灰之力就可以完成

说明

  • 所有连线均为以太网连接
  • 核心网之间设备互联使用192.168.1.0/24点对点配置
  • 核心网每个设备有一个10.0.0.x/32IP用于loopback
  • 图片上的e0/e1/e2分别对应下面配置里的ens3/ens4/ens5

暂时不解决的问题

为了限制篇幅以下问题本文不深入讨论

  • MTU不匹配问题建议PEMSS全部clamp1410
  • CEPE之间的动态路由协议
  • Linux网络栈的开机自动配置动态创建的interfacesysctl配置之类的重启以后都会丢如何使用网络管理软件在开机的时候把它们都正确配置起来就留给读者作为练习了
  • VPLSLinux目前还不支持pseudowire接口

LittleWolf[ Linux ] 使用 Debian Linux 构架 MPLS L3 网络一文中演示了核心网络使用IS-IS以及CEPE之间使用BGP实现动态路由的配置推荐和本文一起阅读

参考软件

图中所有设备使用以下软件

  • Debian 10Linux 4.19.0
  • iproute2 4.20.0
  • FRRouting 7.2.1FRRouting提供的deb

MPLS配置流程

以下每个代码块开头用#标识该代码块在什么环境下运行其中

  • # linux shell表示在Linux原生shellbash下运行
  • # vtysh表示在vtysh下运行
  • # vtysh config表示在vtysh的配置模式conf t下运行

设置操作系统

安装FRRouting

启动FRRouting

加载MPLS内核模块

内核MPLS基础配置

核心网IP网络配置以及设置Loopback IP

Loopback IP其实不是必需的如果你想给每个端口分别配置各种协议的源IP其实也行但是只要你的网络稍微有一点规模配置一下loopback IP可以少打很多其它配置而且能避免router id不稳定导致的动态路由协议玄学问题

P

PE1

PE2

RR

检查

两两邻接的路由器互相ping对面的接口IP应该能够ping

在核心网络中启动IGP

IGP的目的是让loopback IP互相能通因为我不会别的协议为了简单起见这里以OSPF为例全默认配置所有路由都放到Area 0就可以了因为上面端口配置部分我们给所有核心网路由器之间的以太网链路配置了点对点连接OSPF这边需要改一下每个接口的类型

P

PE1

PE2

RR

检查

核心网里的每台路由器使用自己的loopback IPping任意其它路由器的loopback IP应该都能通例如在PE1ping RR

启动MPLS处理功能和LDP服务

每个有MPLS流量经过的接口都要启用MPLS功能并且打开LDP

P

PE1

PE2

RR

其实RR本来不用被掺和进这个MPLS网络的它只要到所有PEIP网络都通即可但是FRRouting当前版本的LDP daemon不支持Downstream Unsolicited模式只能运行在 Downstream-on-Demand模式下所以如果RR不配置MPLS的话PERR方向的包会在P上因为转发表里面没有该条目而被丢掉迫不得已我们把RR也拖下水

如果不想把RR拉下水呢可以在RR上不运行OSPF让和RR直连的那台P或者PE把到RR的静态路由分发到网内或者可以用 label local allocate for 强制LDP分发到RRprefixlabel

检查

两两邻接的路由器之间LDP会话应该已经成功建立例如在P

应该会看到三个会话

并且所有路由器之间使用loopback IP互相ping应该仍然能通这个时候使用loopback IP的包已经会从MPLS网络转发而不是走原来的IP网络了

PE上配置iBGP

iBGP只需要在PE上配置P完全不用配置这是MPLS的好处之一核心路由器只需要处理转发流量不需要再费心思存储路由表和处理路由表更新iBGP呢用full mesh也行RR也行既然图片上画了RR这里就用RR的配置方法所有PERR启动BGP会话本文只演示IPv4的配置方法对每个会话禁用IPv4 unicastSAFI启动IPv4 VPNVPNv4SAFIIPv6配置方法类似启用IPv6 VPNSAFI即可不再赘述

对于生产环境而言单台RR是一个非常容易出问题的单点故障源Packet Pusher有很不错的关于RR冗余架构设计的文章可供参考

RR

PE1PE2

检查

BGP会话应当正常建立

PE上配置VRF

Linux配置VRF有两种方法一种是net namespace一种是VRF interface前者是Linux专有的抽象方式在多个net namespace之间实现路由泄露和数据交换很不方便但是对于容器和半虚拟化等应用场景很实用由于这里只是把Linux系统作为路由器使用本文选择了相对更加贴近商业路由器系统设计的VRF interface模式

配置的思路很简单每个VRF interface在创建的时候会被关联到一个路由表table ID唯一标识然后就像把二层端口加入bridge一样你可以把三层端口加入VRF为了保证新建立的路由表能被特定程序读取这边默认会往路由表写一条metrics设为最大值的默认路由这样既能保证路由表存在又不影响其它路由的正常使用

为了防止操作失误我们在每台PE上对同一个客户使用同样的VRF名称和table ID

PE1

PE2

把客户的路由重分发到核心网的VRF路由表

限于篇幅只演示connected路由的重分发不在PECE之间配置动态路由协议了这里要做的事情是在每个VRF上创建一个空的没有peerBGP配置VRF内要分发的路由先导入该BGPRIB然后让它把路由表打上特定route distinguisher community以后导入主BGPRIB

为了防止操作失误我们在每台PE上对同一个客户使用同样table ID对应RTRD另外需要注意的是FRRoutingvtysh虽然语法上和Cisco IOS很像但是在这里是不太一样的

PE1PE2

配置的时候FRRouting会报一个错不要惊慌这是个bug功能还是能用的

检查

这时候我们应该可以在每个PE上看到每个VRF里面出现通过BGP重分发来的connected路由了例如在PE1

应该会看到类似这样的输出

配置模拟的客户侧设备

为了模拟MPLS对不同客户相同IP段的隔离功能我们把两个客户同一侧的设备的IP配置配成完全相同

customer1-site1customer2-site1

customer1-site2customer2-site2

检查

首先我们确认同一个客户的两个站点之间可以互相ping由于我们的配置启用了PHPTTL propagation同一个客户的两台设备中间经过三台MPLS路由器以后会出现两跳非IP路由所以如果我们使用traceroute或者mtr来测试的话会看到以下输出

可惜的是Linux部分支持RFC4884完全不支持RFC4950所以traceroute -emtr --mpls没法显示路上的MPLS详细信息

如果想要简单地验证一下确实是同一个客户的两个站点被连接起来了的话可以使用netcat启动一个简单的TCP聊天服务只需要在一个客户的其中一个站点启动服务器

然后在同一个客户的另一个站点连接服务器

连接上以后在两边输入任意字符并按回车对端都会显示相同的字符

MPLS……

Linux的发展史可以概括为一个小故事某天你想要造个模型飞机玩就照着真飞机的样子用木棍和纸片随手搭了一个起来放在后院第二天起床一看昨天随手搭的那个飞机已经被邻居开上了天上面坐着十几二十个乘客还有两三个人正趴在机翼上边维持平衡边修发动机呢

反正是免费提供的摔下来也不关我事管他呢你在心里默默地想然后该干啥干啥去了


致谢

两位CCIE大佬对本文的写作做出了巨大贡献

参考

使用Linux作为MPLS路由器8个想法

  1. Pingback引用通告 [ Linux ] 使用 Debian Linux 构架 MPLS L3 网络 | LittleWolf Network Universe

  2. 小网

    en3 en4 en5 接口是如何创建的 且保证设置的IP地址对于其他P PE节点可以访问到 我的实验环境是 云平台上的几台虚拟机

    回复
  3. joshu

    拓扑A—-B—-C这样的连接关系
    差不多是按上面的方案配完之后尝试从AClo接口IPping报文但发现在经过B的时候B没有向B-C接口转发
    A主路由表上有Clo接口IPencap mpls的路由条目B上没有任何encap mpls条目
    B上的ip -M route上的路由表看起来是正常的但其实我也不太清楚什么样的算是正常的
    是否有什么思路能解决为什么不转发的原因
    rp_filter因为我长期做ip base的策略路由这个肯定是一直为0具体接口的mpls accept感觉应该也调成1

    回复
    1. James Swineson 文章作者

      如果你全网关闭了 PHP 功能的话B 上确实不应该有 encap MPLS因为 A 发出来的包已经 encap mpls 了B 会直接收到 MPLS 包然后根据 MPLS 转发表转发没有 encap 过程

      Debug 思路大概是这样首先看 A 上面 ip 路由表看去 C 的包被 encap 了具体哪个 label 以及下一跳是否正确然后去 B 上 FRR 里面用 show mpls ldp ipv4 binding 命令查看 MPLS 转发表根据 local label 对应 A 发过来的 label检查 remote label发给 C 的 label和 nexthop 是否正确以及该条条目 in use 是否为 yes

      回复
      1. joshu

        续一下上次的问题
        BtcpdumpA发过来的mpls标签为52MPLS (label 52, exp 0, [S], ttl 64)
        对应规则是ipv4 172.22.0.4/32 172.22.0.4 52 imp-null yes
        请问remote labelimp-null是否为预期的结果呢
        destination看起来都是正确的nexthop如果这里应该显示Cloopback IP每台机器上的discoveryIP都设置为loopbackIP只不过这里我没有用dummy接口而是用了netplan无网卡的bridges来模拟loopback接口感觉应该没什么关系的话那应该也是对的
        B上的ip -M route的结果是
        52 via inet 10.0.254.77 dev dev-BtoC proto ldp
        这个倒是符合预期的

        拓扑
        Alo 172.22.0.1
        Blo 172.22.0.3
        Clo 172.22.0.4

        回复
        1. James Swineson 文章作者

          如果你是从 A ping C 的 loopback IP 获得这样的结果并且 PHP 开着的话出现 imp-null 是合理的因为这个包是直接到 C 本机的所以 B 那里就会直接把 MPLS 头拆掉以裸 IP 报文形式发给 C

          回复
          1. joshu

            最后发现似乎是
            net.core.default_qdisc = fq
            可能触发mpls包不转发的问题sysctl里不加这一条似乎就正常了很迷惑的问题因为总是开着bbr所以之前没意识到这个问题
            在全网开启no-php-flag的时候
            如果加上这条对于gre隧道可能导致包在目标虚拟网卡转发的时候直接被目标虚拟网卡drop计数增加gre外层的网卡看不到包对于tap网卡则是在转发过程中被丢掉了即在目标网卡中看不到包

发表回复

您的邮箱地址不会被公开 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论了解你的评论数据如何被处理