UPF Plugins 代码分析

用户面处理 Node Graph(N3/N6/N9)

ASTRI_UPF_operation_node_image1.png

注:本问中以 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 沟通时一并澄清。

ASTRI_UPF_operation_node_image2.png

  • 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

问题

  1. DPI 目前支持状态?

  2. N4 接口 UPF 实现框架?

在 ASTRI 答疑时,一并咨询澄清。

注:后续随着继续代码深入,后同步更新该文档