UPF Plugins 代码分析
用户面处理 Node Graph(N3/N6/N9)
注:本问中以 ip4 为例,ip6 架构一至,由类似的节点实现。
各节点功能:
ip4-udp-lookup
: 通过 UDP 报文端口区分业务类型,将 GTPU(UDP port 2152)报文送至 gtpu4-input 节点。
gtpu4-input
: 本节点为 upf 模块开发的 gtpu4-input 节点,非 vpp 原生 gtpu4-input 节点;通过 Message Type 区分 GTPU 报文是
1)echo -request,2) end-marker,3)以太网报文,4)PDU 业务类型;并对应送入相应的 node 节点处理
upf-ip4-input
:区分 ipv4 或是 ipv6,作为 dpi 节点入口,然后送入 dpi 节点,目前astri upf 中 dpi 节点只有两类 均指向 upf-pdr-detect,感觉应该是 dpi 功能还没完善,已作为问题记录下来,后续同 astri 沟通时,咨询清楚原因。
upf-pdr-detect
:通过 vnet_buffer_opaque_t 元数据中存储的 ip.adj_index[VLIB_TX] 匹配到 通过 N4 建立的 session,然后 一次匹配 session 中关联的 pdr,对于 匹配上 pdr的报文送至 upf-ip4-process 节点,未匹配上 pdr 的报文做丢弃处理。
upf-ip4-process
:处理报文 通过 匹配上的 pdr 中的 pdi,以及 far ,edit 报文,然后送至下一节点 upf4-encap
upf4-encap
:实现 报文头的重组(涉及 3层头,4层头),然后送至 ip4-lookup
ip4-lookup
:按照 VPP 原生逻辑,查找路由转发。
控制面处理 Node Graph(N4)
ASTRI UPF 中有完整的 PFCP 协议栈,upf-sx4-input 节点 是 upf 中完整 PFCP 协议栈处理的入口,看上去 UPF 本身已经具备了,处理 PFCP 协议报文的能力,这与 ASTRI 给到的信息不符 (ASTRI 给到的信息是 会外挂一个 golang 实现的 UPF-agent 处理 PFCP 协议栈,再由 UPF-agent 和 UPF 交互实现 N4 相关配置),已记录该问题,和 ASTRI 沟通时一并澄清。
PFCP Session establishment request 处理流程:
/*upf_pfcp_input.c*/
sx46_input_inline // N4 接口报文处理节点
| /*upf_pfcp_server.c*/
|_upf_pfcp_handle_input // 处理端点信息
| /*upf_pfcp_thread.c*/
|_pfcp_send_sx_msg_to_thread // 将报文转发到相应的处理线程
| /*upf_pfcp_server.c*/
|_upf_pfcp_server_rx_msg // 解析报文头
| /*upf_pfcp_api.c*/
|_upf_pfcp_handle_msg // 区分节点级消息或会话级消息
|_session_msg // 会话级消息处理
|_handle_session_establishment_request // 解析 pfcp session establishment request 消息
|_sx_get_association // 获取(发送方的) node id
|_sx_create_session // 获取(核心网分配的) seid
|_sx_update_session
|_handle_create_pdr
|_handle_create_far
|_handle_create_urr
|_handle_create_qer
|_handle_create_bar
|_ ……
| /*回复request请求*/
| /*upf_pfcp_server.c*/
|_upf_pfcp_send_response // 编码 reson 报文
|_upf_pfcp_send_data // 处理 vpp 发送
PFCP Session modification request 处理流程:
/*upf_pfcp_input.c*/
sx46_input_inline // N4 接口报文处理节点
| /*upf_pfcp_server.c*/
|_upf_pfcp_handle_input // 处理端点信息
| /*upf_pfcp_thread.c*/
|_pfcp_send_sx_msg_to_thread // 将报文转发到相应的处理线程
| /*upf_pfcp_server.c*/
|_upf_pfcp_server_rx_msg // 解析报文头
| /*upf_pfcp_api.c*/
|_upf_pfcp_handle_msg // 区分节点级消息或会话级消息
|_session_msg // 会话级消息处理
|_handle_session_modification_request // 解析 pfcp session modification request 消息
|_sx_update_session
|_handle_create_pdr
|_handle_update_pdr
|_handle_remove_pdr
|_handle_create_far
|_handle_update_far
|_handle_remove_far
|_handle_create_urr
|_handle_update_urr
|_handle_remove_urr
|_handle_create_qer
|_handle_update_qer
|_handle_remove_qer
|_handle_create_bar
|_handle_update_bar
|_handle_remove_bar
|_……
| /*回复 request 请求*/
| /*upf_pfcp_server.c*/
|_upf_pfcp_send_response // 编码 reson 报文
|_upf_pfcp_send_data // 发送 response 报文
PFCP Session deletion request 处理流程:
/*upf_pfcp_input.c*/
sx46_input_inline // N4 接口报文处理节点
| /*upf_pfcp_server.c*/
|_upf_pfcp_handle_input // 处理端点信息
| /*upf_pfcp_thread.c*/
|_pfcp_send_sx_msg_to_thread // 将报文转发到相应的处理线程
| /*upf_pfcp_server.c*/
|_upf_pfcp_server_rx_msg // 解析报文头
| /*upf_pfcp_api.c*/
|_upf_pfcp_handle_msg // 区分节点级消息或会话级消息
|_session_msg // 会话级消息处理
|_handle_session_deletion_request // 解析 pfcp session deletion request 消息
|_sx_disable_session
|_……
| /*回复request请求*/
|_upf_pfcp_send_response // 编码 response 报文
|_upf_pfcp_send_data // 发送 response 报文
控制面激活预定义规则:
“预定义规则可由 PCF 激活,即 PCF 将 Predefined PCC Rules 下发至 SMF ,SMF 将其转换为 PDRs 、 QERs 、 FARs 、 URRs 后,在 PFCP 会话建立/更新请求下发至 UPF 并激活……为了减少 SMF 和 UPF 间的 N4 接口信令内容,预定义规则也可以通过在 UPF 上配置通用的报文处理策略(即本地静态规则,包括 PDRs 、 QERs 、 FARs 、 URRs ), SMF 可以通过在 PFCP 会话建立/更新请求的 Create/Update PDR IE 中携带 Activate Predefined Rules IE ,激活 UPF 上的本地静态规则。” —— 6.1.4.4 《面向垂直行业的N4接口规范》
1)、 SMF 在 PFCP 会话建立时下激发UPF上的预定义规则:
/*upf_pfcp_api.c*/
handle_create_pdr
|_if (ISSET_BIT (pdr->grp.fields, CREATE_PDR_ACTIVATE_PREDEFINED_RULES)) // 如果有配置 PDR 激活预定义规则
|_vec_foreach(dnn, gtm->dnn) // 遍历网络名称
| |_vec_foreach(rule_id, dnn->rule_id) // 遍历 dnn 的规则链
| |_if (vec_is_equal (rule_id->rule, pdr->activate_predefined_rules)) // 预定义规则 id 在规则链中存在
| |_hit = 1 // 命中置位
|_hash_get_mem // 如果有命中 rule_id ,则从 gtm(upf_main) 中获取并赋值给 p
|_vec_foreach (qer_id, pr->qer_ids) // 如果 p 中有规则,遍历 p 中的 qer id
| |_sx_get_rules // 获取规则
| |_sx_get_qer_by_id // 通过 qer_id 获取 qer 规则
| |_sx_create_qer // 如果 qer_id 没有获取到 qer 规则,则创建一个
|_vec_foreach (urr_id, pr->urr_ids) // 如果 p 中有规则,遍历 p 中的 urr id
| |_sx_get_rules // 获取规则
| |_sx_get_urr_by_id // 通过 urr_id 获取 urr 规则
| |_sx_create_urr // 如果 urr_id 没有获取到 urr 规则,则创建一个
|_if (pr->far_id) // 如果 far id 存在,则生效 far
| |_sx_get_rules
| |_sx_create_far
|_if (vec_len (pr->app_id)) // 如果 app id 长度不为0,将这个 predefined rules 添加到 pfd_list 中
|_hash_get_mem // 依据 app_id 从内存中获取 pfd 并赋值到 p
|_vec_add1 // 获取 pfd 并添加到tmp_pfd_list 中
2)、SMF 在 PFCP 会话更新时下激发UPF上的预定义规则:
/*upf_pfcp_api.c*/
handle_update_pdr
|_if (ISSET_BIT (pdr->grp.fields, UPDATE_PDR_ACTIVATE_PREDEFINED_RULES)) // 如果有配置 PDR 激活预定义规则
|_hash_get_mem // 从 gtm(upf_main) 中获取并赋值给 p
|_vec_foreach (qer_id, pr->qer_ids) // 如果 p 中有规则,遍历 p 中的 qer id
| |_sx_get_rules // 获取规则
| |_sx_get_qer_by_id // 通过 qer_id 获取 qer 规则
| |_sx_create_qer // 如果 qer_id 没有获取到 qer 规则,则创建一个
|_vec_foreach (urr_id, pr->urr_ids) // 如果 p 中有规则,遍历 p 中的 urr id
| |_sx_get_rules // 获取规则
| |_sx_get_urr_by_id // 通过 urr_id 获取 urr 规则
| |_sx_create_urr // 如果 urr_id 没有获取到 urr 规则,则创建一个
|_if (pr->far_id) // 如果 far id 存在,则生效 far
| |_sx_get_rules // 获取规则
| |_sx_create_far // 如果 far_id 没有获取到 far 规则,则创建一个
|_if (vec_len (pr->app_id)) // 如果 app id 长度不为0,将这个 predefined rules 添加到 pfd_list 中
|_hash_get_mem // 依据 app_id 从内存中获取 pfd 并赋值到 p
|_vec_add1 // 获取 pfd 并添加到 tmp_pfd_list 中
IE 解析
– FAR
— CREATE FAR :
/*upf_pfcp_api.c*/
handle_create_far // session 级 FAR 创建
|_vec_foreach (create, create_far) // 遍历 session 级 FAR IE
|_far.id = create->far_id // 设置变量 far 的id
|_far.apply_action = create->apply_action; // 设置变量 far 对报文的操作
|_FORWARDING_PARAMETERS_DESTINATION_INTERFACE_TYPE // 设置变量 far 的转发目的接口类型
|_FORWARDING_PARAMETERS_FORWARDING_POLICY // 设置变量 far 的转发策略
|_FORWARDING_PARAMETERS_NETWORK_INSTANCE // 设置变量 far 的网络实例
|_FORWARDING_PARAMETERS_DESTINATION_INTERFACE // 设置变量 far 的转发目的接口
|_FORWARDING_PARAMETERS_REDIRECT_INFORMATION // 设置变量 far 的重定向信息
|_FORWARDING_PARAMETERS_OUTER_HEADER_CREATION // 设置变量 far 的外部数据包头
|_FORWARDING_PARAMETERS_TRANSPORT_LEVEL_MARKING // 设置变量 far 的IP报头DSCP标记
|_FORWARDING_PARAMETERS_HEADER_ENRICHMENT // 设置变量 far 的头增强参数
|_init_far_buffering_list(&far) // 从 upf_main 的 buffer 中,分配 buffer 给 far
|_sx_create_far(sess, &far) // 创建 far
— UPDATE FAR :
/*upf_pfcp_api.c*/
handle_update_far
|_vec_foreach (update, update_far) // 遍历 session 级 FAR IE
|_sx_get_far // 依据 far id 获取要更新的 far ,存放到 upf_far_t *far 中并对其进行修改。
|_UPDATE_FAR_APPLY_ACTION // 获取要更新的 far 中对报文的操作
|_UPDATE_FAR_UPDATE_FORWARDING_PARAMETERS // 如果要更新的 far 是转发操作
| |_UPDATE_FORWARDING_PARAMETERS_FORWARDING_POLICY \
| / UPDATE_FORWARDING_PARAMETERS_NETWORK_INSTANCE // 修改 far 转发策略
| |_UPDATE_FORWARDING_PARAMETERS_DESTINATION_INTERFACE // 或者修改网络接口
| |_UPDATE_FORWARDING_PARAMETERS_REDIRECT_INFORMATION // 修改重定向信息
| |_UPDATE_FORWARDING_PARAMETERS_OUTER_HEADER_CREATION // 修改外部数据包头
| |_UPDATE_FORWARDING_PARAMETERS_TRANSPORT_LEVEL_MARKING // 修改 IP 报头标记
| |_UPDATE_FORWARDING_PARAMETERS_HEADER_ENRICHMENT // 修改头增强参数
|_UPDATE_FAR_APPLY_ACTION // 如果要更新的 far 是缓存操作
|_far->bar_id = OPT (...) // 更新 bar id
— REMOVE FAR :
/*upf_pfcp_api.c*/
handle_remove_far
|_sx_delete_far //依据 far id 删除 far
问题
DPI 目前支持状态?
N4 接口 UPF 实现框架?
在 ASTRI 答疑时,一并咨询澄清。
注:后续随着继续代码深入,后同步更新该文档