使用mpls功能,首先需要加载mpls相关的模块:

$ sudo modprobe mpls_gso
$ sudo modprobe mpls_iptunnel
$ sudo modprobe mpls_router  

使能mpls的接收和设置labels表项的数量,默认情况下内核不接收mpls报文,如果不使能此项,在如下使用ip命令配置本机环回lo接口接收mpls数据包时就会失败。labels转发表项的数量初始为0,将会导致不能配置label转发表项。

sysctl -w net.mpls.conf.lo.input=1
sysctl -w net.mpls.platform_labels=1048575

配置1:到网络10.1.1.0/30的数据包增加200和300两个label

$ sudo ip route add 10.1.1.0/30 encap mpls 200/300 via 192.168.1.1 dev ens33
$
$ ip route
10.1.1.0/30  encap mpls  200/300 via 192.168.1.1 dev ens33
$ 

使用ping测试,tcpdump抓包内容可见,增加了200和300两个label,标签300设置了标签栈底标志[S]。

$ ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
$
$ sudo tcpdump mpls -vve
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
23:36:30.813147 00:0c:29:74:7f:04 (oui Unknown) > 00:90:27:fe:c9:34 (oui Unknown), ethertype MPLS unicast (0x8847), length 106:
MPLS (label 200, exp 0, ttl 64)(label 300, exp 0, [S], ttl 64)(tos 0x0, ttl 64, id 36751, offset 0, flags [DF], proto ICMP (1), length 84)localhost > localhost: ICMP echo request, id 6907, seq 1, length 64

配置2:标签为500的mpls数据包送到本机的环回接口lo:

$ sudo ip -f mpls route add 500 dev lo
$
$ip -f mpls route show
500 dev lo
$

配置3:标签为100的数据包替换为标签600,发往192.168.1.2。

$ sudo ip -f mpls route add 100 as to 600 via inet 192.168.1.2
$
$ip -f mpls route show
100 as to 600 via inet 192.168.1.2 dev ens33
500 dev lo
$

配置4:指定多个下一跳地址,为每个下一跳指定不同的(90/91)标签:

$ sudo ip -f mpls route add 80 nexthop as to 90 via inet 192.168.1.2 nexthop as to 91 via inet 192.168.1.3
$
$ ip -d -f mpls route show
unicast 80 proto boot scope global nexthop as to 90 via inet 192.168.1.2  dev ens33nexthop as to 91 via inet 192.168.1.3  dev ens33
$

MPLS的路由结构
内核函数mpls_route_add处理MPLS路由的添加工作。所有的MPLS路由全部组织在网络命名空间的成员platform_label指针数组(指针的指针)中,通过标签值可找到对应的MPLS路由。全部路由不超过platform_labels指定的最大数量。

struct netns_mpls {size_t platform_labels;struct mpls_route __rcu * __rcu *platform_label;
}

一个mpls_route路由表项所占空间与其所有的下一跳mpls_nh的空间,以及每个下一跳的所有label空间之总和不超过4K字节大小。mpls_route路由表项结构图如下:

另外,内核注册了netdev通知链函数mpls_dev_notify处理mpls路由的失效和活动状态。

static struct notifier_block mpls_dev_notifier = {.notifier_call = mpls_dev_notify,
};

当接收到设备关闭NETDEV_DOWN事件时,调用mpls_ifdown遍历所有的mpls路由表项,将出口设备为此设备的表项标记为不可用(RTNH_F_DEAD)。如果接收到设备NETDEV_UP的事件,同样的遍历所有的表项,经出口设备为此设备的表项标记为可用,即去掉RTNH_F_DEAD标志。

static int mpls_dev_notify(struct notifier_block *this, unsigned long event, void *ptr)
{struct net_device *dev = netdev_notifier_info_to_dev(ptr);switch (event) {case NETDEV_DOWN:mpls_ifdown(dev, event);break;case NETDEV_UP:mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);break;}
}

MPLS数据帧处理
Linux内核协议栈注册了一个ETH_P_MPLS_UC(0x8847)类型的数据包处理函数(mpls_packet_type)处理MPLS报文。

static struct packet_type mpls_packet_type __read_mostly = {.type = cpu_to_be16(ETH_P_MPLS_UC),.func = mpls_forward,
};

首先通过MPLS标签解析mpls_entry_decode函数,得到数据包中MPLS标签的4个字段:标签值、TTL、流量类别和标签栈底标志位。标签值字段占用20个bit位,其最大值为2**20 -1,由于mpls协议存在预留值,合法的标签值从16开始(MPLS_LABEL_FIRST_UNRESERVED)。

static inline struct mpls_entry_decoded mpls_entry_decode(struct mpls_shim_hdr *hdr)
{    struct mpls_entry_decoded result;unsigned entry = be32_to_cpu(hdr->label_stack_entry);result.label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT;result.ttl = (entry & MPLS_LS_TTL_MASK) >> MPLS_LS_TTL_SHIFT;result.tc =  (entry & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT;result.bos = (entry & MPLS_LS_S_MASK) >> MPLS_LS_S_SHIFT;return result;
}

其次通过得到的MPLS标签值,查询mpls路由表。有之前介绍的mpls路由表结构可知,以标签值做索引即可找到对应的路由项:

static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
{struct mpls_route __rcu **platform_label = rcu_dereference(net->mpls.platform_label);rt = rcu_dereference(platform_label[index]);return rt;
}

对于仅有一个下一跳的路由表项,直接使用。对于多个下一跳地址,需要在找到的路由项中,选择一个下一跳地址,函数(mpls_select_multipath)完成此功能。检查如果活动的下一跳的个数为0,返回空。否则根据数据包中的以下字段计算hash值:最多4个标签值、IP头的源IP地址、目的IP地址和协议号。得到的hash值与当前活动的下一跳数量(alive)取余得到下一跳地址的索引值。mpls_get_nexthop函数取出mpls路由的下一跳。

如果活动的下一跳数量与下一跳总数相等,说明根据下一跳索引nh_index可以直接取到下一跳结构。否则,需要遍历所有活动的下一跳列表,找到此下一跳。

    alive = READ_ONCE(rt->rt_nhn_alive);if (alive == 0) return NULL;hash = mpls_multipath_hash(rt, skb);nh_index = hash % alive;if (alive == rt->rt_nhn) goto out;for_nexthops(rt) {unsigned int nh_flags = READ_ONCE(nh->nh_flags);if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))  continue;if (n == nh_index) return nh;n++;} endfor_nexthops(rt);
out:return mpls_get_nexthop(rt, nh_index);

如果配置了标签替换,仅替换数据包中的顶层标签。以下代码将mpls路由表项中下一跳对应的标签栈写入到数据包头中。

    hdr = mpls_hdr(skb);bos = dec.bos;for (i = nh->nh_labels - 1; i >= 0; i--) {hdr[i] = mpls_entry_encode(nh->nh_label[i], dec.ttl, 0, bos);bos = false;}

环境
iproute2版本:4.17.0
内核版本:  Linux-4.15

Linux MPLS 初探相关推荐

  1. Linux 文件系统初探

    文章目录 文件系统 Linux文件系统标准结构 所有分区的含义 Linux分区 文件系统 Windows Linux 分盘,每个驱动器有自己的根目录,形成的是多个树并列的结构. 只有一个根目录 / , ...

  2. Linux内核初探3

    1.内核的任务 内核:充当底层的驱动程序,应用程序只与内核有联系,内核是应用程序的所知道的层次结构中最底层的,也是资源管理程序,也是一种库,提供了一组面向系统的命令. 2.实现策略 微内核(中央内核) ...

  3. linux内核开文件系统,内核必须懂(二): Linux文件系统初探

    目录 前言 文件系统结构 新建文件和inode 文件创建过程 inode解析 打开文件 参考 最后 前言 这次来说文件系统. 文件系统是非常重要的, 提高磁盘使用率, 减小磁盘磨损等等都是文件系统要解 ...

  4. linux驱动初探之字符驱动

    关键字:字符驱动.动态生成设备节点.helloworld linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~ 此驱动程序基于linux3. ...

  5. linux 基础初探

    linux 看了又看,学了又学,脑袋不灵光,太容易忘记,所以尝试可不可以将厚书学薄点,复习一遍也来梳理一遍! 第一部分 linux 规划与安装就略过了 第二部分 Linux 文件.目录与磁盘格式,共四 ...

  6. Linux文件管理初探---学习文件管理,我想你必须要知道的目录

    前言:在Linux系统中,文件管理的功能是必不可少的,包括创建目录与文件.复制与移动文件.删除文件与目录等.所以我们必须知道在Linux系统中,哪些目录是正规系统会存在的,以及该目录应该存放在哪些信息 ...

  7. LINUX CMA 初探

    一.什么是CMA CMA,Contiguous Memory Allocator,是内存管理子系统中的一个模块,负责物理地址连续的内存分配.一般系统会在启动过程中,从整个memory中配置一段连续内存 ...

  8. linux内核的论文,Linux kernel 初探

    在了解内核之前,先要会编译.调试内核. 编译内核 安装依赖: sudo apt-get update sudo apt-get install git fakeroot build-essential ...

  9. 熟悉linux指令游戏,Linux指令初探之闯关游戏Bandit(上)

    开学季,Evan会带领小萌新们一点点接触安全领域,今天要给大家讲的是和Linux有关的一款游戏Bandit,通过Bandit你会学到Linux的一些基础指令,这对今后的学习有很大的帮助,准备好了吗让我 ...

  10. Linux内核编程(二)-----------Linux内核初探

    写在前面:本篇主要介绍Linux内核的开发模式.linux代码的组成.vmlinux  zImage  uImage的区别,以及编译下内核. 正文: 一.Linux内核的开发模式 1.git:是一个分 ...

最新文章

  1. 用python创建数据库监控平台(3)安装Python3.5
  2. GridView中实现单选RadioButton
  3. VS+QT和qtcreator工程的互相转换
  4. redhat 服务器 安装mysql_Linux服务器安装mysql
  5. 模型OnMouseXXX事件
  6. Python 中xrange和range区别
  7. nginx+tomcat+redis负载均衡,实现session共享
  8. python 做界面时如何使图片保持透明背景_Python matplotlib生成图片背景透明的示例代码...
  9. python中构造函数可以重载吗_python中的函数重载了吗?
  10. 在控制台环境下调用MFC DLL崩溃的问题小结
  11. 计算机类专业毕业设计(学期课程设计)题目大全
  12. 新中大怎么修改服务器,新中大软件服务器地址修改
  13. 如何使用Git Pull覆盖本地文件
  14. SecureCRT连接交换机Console口
  15. 清除显卡右键菜单批处理.bat
  16. pycharm ssh interpreter 搭建
  17. 从0到1:朋友圈爆款背后的计算机视觉技术与应用 | 百万人学AI
  18. 十五天学会Autodesk Inventor,看完这一系列就够了(九),主题/色彩/单位设置
  19. 基于OpenCV的鱼眼相机畸变矫正(含代码)
  20. 计算机驱动空间不够,为何我的电脑在安装显卡驱动的时候就是安装不成功,提示是磁盘空间不足,但是其余的磁盘都有130GB左右...

热门文章

  1. poj 2485 Highways 超级大水题 kruscal
  2. 如何往一个指定的地址写入一个值呢
  3. 全国省市县无刷新级联菜单
  4. PHP程序的常见漏洞攻击分析
  5. Java之品优购课程讲义_day03(6)
  6. chaos-monkey-spring-boot小试牛刀
  7. 深入学习之mysql(二)表的操作
  8. java jvm学习笔记三(class文件检验器)
  9. 分享:BlackHole开发日记-Java守护进程、Signal处理
  10. 关注SharePoint 2010 ,更要关注InfoPath 2010!