SREXT 实现分析

文章介绍了如何在Linux内核中实现SRv6服务链(SFC)的SREXT模块,该模块处理unawareSRv6服务,通过维护本地SID表进行SRv6报文的封装和解封装。详细阐述了SREXT的安装、卸载步骤,以及测试部署过程。此外,还解析了SREXT的代码逻辑,包括钩子函数、协议栈交互和路由处理流程。
摘要由CSDN通过智能技术生成

要在 linux 中实现 SRv6 SFC 的模型, linux 从4.10版本开始已经支持了 SRv6 协议栈,大部分 SRv6 的转发节点功能都具备。不过目前要使用 SFC ,还有许多服务未支持 SRv6 甚至未支持 IPv6 ,也就是许多服务还是 unaware SRv6 类型的。

为了能让 unaware SRv6 SFC 也能接入,需要有一个代理来完成 SRv6 剥除并且记录通信值如指针等, SREXT 就实现了这个功能。

SREXT 以模块的方式载入内核,维护了一个本地 SID 表,如果是下一跳 SFC 是 unaware 类型,就剥除 SRv6 并记录指针、 SID List 等内容,待下一跳返回后重新封装 SRv6 ,使用记录的 SID List ,和记录的指针值再减一写入 SRH 中。

本文将研究 SREXT 的实现原理。

代码库:

https://github.com/netgroup/SRv6-net-prog/tree/kernel-4_18

安装步骤:

$ git clone https://github.com/netgroup/SRv6-net-prog 
$ cd srv6-net-prog/srext/
$ sudo make 
$ sudo make install
$ sudo depmod -a
$ sudo modprobe srext

卸载步骤:

$ sudo rmmod srext
$ sudo make deinstall
$ sudo make clean

测试部署:

https://netgroup.github.io/SRv6-net-prog/testbed-basic.html

部署运行步骤:

$ vagrant box add srv6-net-prog http://cs.gssi.infn.it/files/SFC/srv6-net-prog.box
$ cd SRv6-net-prog/vagrant-box/testbed1/
$ vagrant up # 需要等待比较长的时间,如果中间有 ssh 相关的错误,有可能安装完机器后登录退出卡住了,多运行几次就可以。

如果部署成功,运行 vagrant status 可以看到如下效果:

执行如下步骤来初始化 ingress 虚机:

$ vagrant ssh ingress 
$ cd SRv6-net-prog/srext/scripts/testbed1/
$ sudo ./setup_ingress_testbed1.sh

执行如下步骤来初始化 nfv 虚机:

$ vagrant ssh nfv 
$ cd SRv6-net-prog/srext/scripts/testbed1/
$ sudo ./setup_nfv_testbed1.sh

执行如下步骤来初始化 egress 虚机:

$ vagrant ssh egress 
$ cd SRv6-net-prog/srext/scripts/testbed1/
$ sudo ./setup_egress_testbed1.sh

执行完上述步骤后,模型就部署成功了,接下来就可以在 ingress 虚机中执行 $ sudo ip netns exec client ping6 b::2 验证测试了。

流量模型:

在 ingress 虚机中执行 $ sudo ip netns exec client ping6 b::2 时,流量模型如下:

请求过程:

  1. 在 ingress 虚机中,数据包通过 ip route 的 encap 配置封装了 srV6 报文;

  1. 在 nfv 虚机中,srext 模块依据 Local SID Table 的配置将数据包剥除 SRv6 部分并转发给各个 unaware vnf 节点,在 unaware vnf 节点中抓包,抓到的是 client 发出的源数据包。 Srext 剥除 SRv6 时,会记录好数据包的 SID List 和指针信息。

  1. 当 unaware vnf 将数据包处理完之后, srext 接收到该报文,依据 Local SID Table 的配置,取出之前存储记录的数据,将剥除了的 SRv6 报文再次加上,并且指针值减一,目的地址更新为下一跳 SID ;

  1. 在 egress 虚机中, srext 模块依据 Local SID Table 的配置剥除数据包 SRv6 部分,并转发给目的 server 。

应答过程与请求过程相似。

代码分析:

Srext 模块与 linux 系统协议栈的交互流程:

在上述三个节点中, srext 的运作流程如下:

  1. Srext 模块注册了两个钩子函数在 IP_PRE_ROUTING 结点上,分别是处理 ipv4 协议的 hook_v4_pre_routing 和处理 ipv6 协议的 sr_pre_routing 函数。在示例中,主要使用 ipv6 的 sr_pre_routing 函数。

  1. sr_pre_routing 函数负责从本地 SID 表,并执行其中的节点操作:

  1. 首先使用接口名称去查表,如果接口在 Local SID Table 中有记录,则执行 behaviour 字段的操作;

  1. 如果接口名称查表查不到,就拿数据包的目的 IP 再去查询 Local SID Table ,查到则执行 behaviour 字段操作;

  1. 数据包在 sr_pre_routing 如果有命中 Local SID Table ,执行完数据包操作之后会发往 IP_LOCAL_INPUT 本机接收;如果没有命中 Local SID Table ,则会查询 route 子系统,继续 linux 系统的转发操作。

代码调用流程:

srext_init
|_ nf_register_net_hook(&init_net,&sr_ops_pre);

# 注册了 sr_pre_routing 钩子函数
sr_ops_pre.hook = sr_pre_routing;
sr_ops_pre.pf = PF_INET6;
sr_ops_pre.hooknum = NF_INET_PRE_ROUTING;
sr_ops_pre.priority = NF_IP_PRI_LAST;

sr_pre_routing
|_ sdev = sdev_lookup(skb->dev->name); # 依据接口名称查表
   |_ if (sdev == NULL) # 如果找不到就按目的地址查找
   |  |_ s6 = sid_lookup(iph->daddr);
   |     |_ if (s6 == NULL) # 如果目的地址也找不到,就退出 sr_pre_routing 继续执行 linux 系统流程
   |     |  |_ return NF_ACCEPT;
   |     |_ else # 如果依据目的地址查找成功,就执行 sid table 中配置的操作
   |        |_ s6->func(skb, s6))
   |_ sdev->func(skb, sdev); # 如果依据接口查表成功,就执行 sid table 中配置的操作

Linux 协议栈 ip 层处理流程:

测试例子中,第一个节点在 srext 中是没有命中 Local SID Table 的,那么它会结束 srext 调用,继续执行 linux 协议栈的操作。当目的地址非本机时,数据包会进入路由查询流程然后转发,此时,在节点上使用 ip route 命令配置的封包操作就生效了。

以下时 ip route 模块在整个 linux 协议栈中的位置:

代码调用流程:

在例子中,使用了两个 route 命令,分别是:

sudo ip -6 route add b::/64 via 1:2::2 encap seg6 mode encap segs 2::AD6:F1,2::AD6:F2,2::AD6:F3,3::D6
sudo ip -6 route add a::/64 via 2:3::1 encap seg6 mode encap segs 2::,1::D6

这两个命令都作用在本地发出包后的 ip route 中,即上图 IP_LOCAL_OUT 流程之后调用。相关代码如下:

ipv6_exthdrs_init # 扩展头初始化
|_ inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING); # 添加 srh 协议

static const struct inet6_protocol rthdr_protocol = {
        .handler        =        ipv6_rthdr_rcv,
        .flags          =        INET6_PROTO_NOPOLICY,
};

ipv6_rthdr_rcv
|_ ipv6_srh_rcv
   |_ ip6_route_input       seg6_lookup_nexthop
      |_ ip6_route_input_lookup
         |_ fib6_rule_lookup
            |_ lookup(net->ipv6.fib6_local_tbl) # 本地路由一般都配置在 fib local table 中。
            |_ lookup(net->ipv6.fib6_main_tbl) # 非本地路由一般都配置在 fib main table 中。

Linux 添加路由过程:

fn_hash_insert

ip6_route_add
|_ ip6_route_info_create # 初始化 fib6_info 结构体
   |_ fib6_new_table # 如果 table 不存在的话
   |_ fib6_info_alloc


fib6_lookup
|_ fib6_table_lookup
   |_ fib6_node_lookup
   

Reference

https://www.man7.org/linux/man-pages/man8/ip-route.8.html

https://blog.csdn.net/weixin_42652361/article/details/110007778

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值