基于 SRv6 的 SFC 服务链
城域网 SRv6 SFC 应用场景


报文转发过程

-
SC 节点收到用户网络原始报文后,根据服务链信息进行SRv6 报文封装,此时 IPv6 报文的目的地址是 SFF 的 End.AS SID , SRH 头部包括服务链全路径信息( SID List )。 -
SFF 收到报文以后,通过查找 Local SID 表发现目的地址为本地 End.AS SID ,保存当前 SL ( SID List 指针)值,并执行 End.AS SID 对应指令——解封报文,去除 IPv6 报文头部后将原始报文通过配置的出接口发送到 SF 进行处理。 -
当 SF 处理完报文并将报文发回给 SFF , SFF 会根据收报文的入接口查找配置信息,然后依据配置的 SID list 重新封装 SRv6 报文,封装后报文 SRH 中 SID list 与 SC 上 SRv6 TE Policy 路径一致, SRH 中的 SL 值为步骤2中保存的 SL 值减1。此时 SRv6 报文目的地址是本地 End.AS SID 之后的下一个 SID (即 Device C 的 End SID )。SFF 根据报文的目的 IPv6 地址,查找 IPv6 路由表转发报文。 -
( 在真实服务链路径中,不一定每个节点都需要把报文传递到 SF 中,有时可能只是路径需要经过了该节点,因此节点只需查找路由并转发报文到下一个节点即可。图中 Device C 就是模拟这样的节点。) 当 Device C 接收到报文以后,通过查找 Local SID 表发现目的地址为本地 End SID ,按 Srv6 转发流程处理报文。目的地址替换为 D1 (即尾节点的 End SID ), SL-1 ,查找 IPv6 路由表转发报文。 -
尾节点接收到报文以后,通过查找 Local SID 表发现目的地址 D1 为本地 SID ,将目的地址替换为 D2 (即尾节点的 End.DT4 SID ), SL-1 变为0。尾节点执行 End.DT4 SID 对应指令——解封装 SRv6 报文,并将原始报文转发到对应 VPN 。
Linux 中的 SRv6 SFC 模型

测试方法
#!/bin/bash
TMUX=4hs1sw
# Kill tmux previous session
tmux kill-session -t $TMUX 2>/dev/null
# Clean up the previous network namespaces
for ns in h1 h2 h3 h4 switch; do
ip netns delete ${ns} 2>/dev/null
done
ip netns add h1
ip netns add h2
ip netns add h3
ip netns add h4
ip netns add switch
ip -netns switch link add veth1 type veth peer name enp0s8 netns h1
ip -netns switch link add veth2 type veth peer name enp0s8 netns h2
ip -netns switch link add veth3 type veth peer name enp0s8 netns h3
ip -netns switch link add veth4 type veth peer name enp0s8 netns h4
###################
#### Node: h1 #####
###################
echo -e "nNode: h1"
ip netns exec h1 sysctl -w net.ipv6.conf.all.forwarding=1
ip -netns h1 link set dev lo up
ip -netns h1 link set dev enp0s8 address 00:00:00:00:00:01
ip -netns h1 addr add fc00::1/64 dev enp0s8
ip -netns h1 link set dev enp0s8 up
ip -netns h1 -6 route add fcf0:12::100 via fc00::2
# 2 SIDs here!
# - fcf0:12::100 steers the packet to node h2 where the SRv6 End function is
# applied (SL=1 on ingress -> SL=0 on egress)
#
# - fcf0::23::6006 steers the packet to node h3 where the decap function is
# applied (SL=0 on ingress)
ip -netns h1 -6 route add fc00::4 encap seg6 mode encap
segs fcf0:12::100,fcf0:23::6006 dev enp0s8
###################
#### Node: h2 #####
###################
echo -e "nNode: h2"
ip netns exec h2 sysctl -w net.ipv6.conf.all.forwarding=1
ip -netns h2 link set dev lo up
ip -netns h2 addr add fc00::2/64 dev enp0s8
ip -netns h2 link set dev enp0s8 address 00:00:00:00:00:02
ip -netns h2 link set dev enp0s8 up
ip -netns h2 -6 route add fcf0:23::6006 via fc00::3
# apply the SRv6 End function for updating the active SID
ip -netns h2 -6 route add fcf0:12::100 encap seg6local action End dev enp0s8
###################
#### Node: h3 #####
###################
echo -e "nNode: h3"
ip netns exec h3 sysctl -w net.ipv6.conf.all.forwarding=1
ip -netns h3 link set dev lo up
ip -netns h3 addr add fc00::3/64 dev enp0s8
ip -netns h3 link set dev enp0s8 address 00:00:00:00:00:03
ip -netns h3 link set dev enp0s8 up
# apply the SRv6 End.DT6 function to decap the packet and to deliver
# the inner packet to host h4.
ip -netns h3 -6 route add fcf0:23::6006 encap seg6local
action End.DT6 table 254 dev enp0s8
# all the hosts are in the same network...
#
# +-----+---------------------------------+-----+
# - decap packet: | ... | IPv6 DA=fc00:1, SA=fc00::4, ... | ... |
# +-----+---------------------------------+-----+
#
# host h3 tries to route the decap packet and it finds out that such
# packet can be sent directly from node h1 to node h4.
# As a result, the node h3 sends an icmpv6 redirect packet to host h1.
# We do not want to send this packet to the host h1, so we filter it.
ip netns exec h3 ip6tables -t mangle -A POSTROUTING
-p icmpv6 --icmpv6-type redirect -j DROP
###################
#### Node: h4 #####
###################
echo -e "nNode: h4"
ip netns exec h4 sysctl -w net.ipv6.conf.all.forwarding=1
ip -netns h4 link set dev lo up
ip -netns h4 addr add fc00::4/64 dev enp0s8
ip -netns h4 link set dev enp0s8 address 00:00:00:00:00:04
ip -netns h4 link set dev enp0s8 up
#######################
#### Node: switch #####
#######################
echo -e "nNode: switch"
ip -netns switch link set dev lo up
ip -netns switch link set dev veth1 up
ip -netns switch link set dev veth2 up
ip -netns switch link set dev veth3 up
ip -netns switch link set dev veth4 up
ip -netns switch link add sw0 type bridge
ip -netns switch link set dev sw0 up
ip -netns switch link set dev veth1 master sw0
ip -netns switch link set dev veth2 master sw0
ip -netns switch link set dev veth3 master sw0
ip -netns switch link set dev veth4 master sw0
sleep 1
## Create a new tmux session
tmux new-session -d -s $TMUX -n h1 ip netns exec h1 bash
tmux new-window -t $TMUX -n h2 ip netns exec h2 bash
tmux new-window -t $TMUX -n h3 ip netns exec h3 bash
tmux new-window -t $TMUX -n h4 ip netns exec h4 bash
tmux new-window -t $TMUX -n switch ip netns exec switch bash
tmux set-option -g mouse on
tmux select-window -t :0
tmux attach -t $TMUX
模型图


测试方法
云内基于 SRv6 的 SFC 方案

-
控制面可通过 SDN 控制器、 OVS 命令行等向 OVS 下发服务链信息配置流表,包括目标报文标记( eg. 五元组)和服务链信息( SID 列表),服务链信息流表在 OVS 中将转化为端口对数据包的具体操作(封装、解封、转发)。 -
OVS 对接用户的端口(port 1)收到业务流量后,查找流表,如果命中了服务链信息配置流,则封装 SRv6 报文,将服务链路径信息依次全部压入 SRH 中 SID List 字段,初始化 SID List 指针,将 IPv6 目的地址指向 SID List 首节点。将报文转发到下一个处理端口(port 2)。 -
当中间端口(port 2、port 3)收到 SRv6 报文后,依据控制面的配置,这些端口将执行转发动作:将 SID List 指针值减一,替换 IPv6 目的地址,转发到下一个节点。 -
当对接 SF 的端口(port 4、port 5)收到 SRv6 报文后,依据控制面的配置,这些端口将先执行解包动作,保存报文中的 SID 指针等信息,然后发送原始业务报文给对应的 SF ;当 SF 返回原始业务报文后, OVS 再读取保存的信息执行封包动作,然后转发到下一个节点(port 6)。 -
当目标客户机所在端口(port 6)收到 SRv6 报文后,依据控制面的配置,这些端口将执行解包动作,删除包含服务链信息的 SRv6 部分,将原始业务报文转发给目标机器。至此完成基于 SRv6 的 SFC 流程,其过程用户无感知。

测试方法


-
控制面下发 SFC 配置流表到 OVS , 指定从 port 2 接收到的报文需要依据 SID List 执行 SRv6 的封装;从 port 4 收到的报文执行 SRv6 转发动作;从 port 6 收到的报文执行解封操作。 -
当 port 2 收到报文时,检查是否命中 SFC 流表,如果命中则封装 SRv6 报文,并按照流表规则转发到 port3 。其中封装操作将服务链信息全部压入 SRH 中,由于 OVS port2 作为 SID List 的起点,所以初始化指针值为第二个 SID 即下一跳 SID ,并加上 IPv6 报文,且目的地址为下一跳 SID 。 -
Namesapce 2 将 port3 发来的报文转发给 port 4 。OVS port 4 接收到报文后,如果命中 SFC 流表,则执行 SRv6 转发动作:SID List 指针值减一,更新 IPv6 目的地址。修改完 SRV6 报文后, OVS 按照流表规则转发到 port 5。 -
Namespace 3 将 port5 发来的报文转发给 port 6。OVS port 6 接收到报文后,如果命中 SFC 流表,则执行 SRv6 解封动作:剥除 IPv6 和 SRH 部分报文,将原始报文发送到 port 7给目标机器。
模型图


-
控制面配置服务链信息流表到 OVS 中,其中对接业务源的 user1-port 负责将业务报文封装到 SFC 的 SRv6 中,对接目标源的 user2-port 负责将 SFC 的 SRv6 剥除。 -
当业务源中客户端发起访问, OVS 中 user1-port 收到数据包后,查询流表内容,发现数据包命中 SFC 规则,于是将规则中的全量 SID (服务链[2::ad:f1,2::ad:f2,2::ad:f3]和 OVS 中起止 SID [1:2::1, 2:3::2] )依次压入 SRH 栈中,初始化 SID List 指针,封装 IPv6 报文,并将目的地址设置为下一跳 SID 即第一个 SF 地址( 2::ad:f1 )。 -
当 sfc-domain 接收到报文后,程序查找目的 IPv6 ,发现命中 LOCAL SID Table ,于是保存指针,剥除 SRv6 ,发送原始报文到 vnf1 中。当 vnf1 返还原始报文后, sfc-domain 将读取保存的指针信息,重新封装 SRv6 报文,递减指针值,更新下一跳 SID 到目的 IPv6 地址,查找 LOCAL SID Table 和路由表,如果命中就转发到下一跳 SID 所在接口。 -
当 sfc-domain 将报文转发到最后一个 SID (2:3::2)时,将报文通过 proxy1-port 发送到 OVS 中, OVS 查找流表,发现命中 SFC 解包规则,于是剥除 SRv6 部分,将原始报文从 user2-port 发送出去。 至此, tcp 请求过程中的 SFC 处理完毕,业务目标收到报文仍是原始报文,对 SFC 处理无感知。
测试方法
-
配置好 ovs 端口:
https://pan.baidu.com/s/1uxIANuLHiJuJtdM37yEFZQ?pwd=k7xc
-
配置 vm ,并测试:
https://pan.baidu.com/s/1hQRDrpd-HrrFVuGWk9mYeQ?pwd=q5yn
综上,本文讨论了 SFC 服务链应用,及其在 Linux 中的实现方案,以及在云环境中基于 SRv6 的实现方案,以探索算力网络时代下 SFC 在云环境中的快速部署。模型较为简单,开发仍在不断完善中。
Reference
-
《 SRv6 SFC 技术白皮书》
-
https://github.com/netgroup/p4-srv6-usid/issues/4 -
Ip route encap introduction https://www.man7.org/linux/man-pages/man8/ip-route.8.html
