# 门控功能设计方案 ## 需求分析 UPF 应支持基于服务数据流( SDF )或应用业务( APP ID )实现门限控制,以控制通过某一端口上下行的数据流或者业务应用报文的转发或丢弃。 其中,服务数据流,可通过获取 SMF 下发的 " PDR - PDI ( - Ethernet Packet Filter ) - SDF Filter " 字段值,用以识别某一条数据流;应用业务,可通过获取 SMF 下发的 " PDR - PDI - Application ID "字段值,用以识别某一种业务;门限控制,可通过获取 SMF 下发的 " QER - Gate Status "字段值,用以识别对数据流或者业务应用报文的是转发还是丢弃;转发操作,可根据 SMF 下发的 FAR 相关字段值实现。 ## 方案设计: 1、从 N4 接口获取控制面对 PDU Session 的操作规则参数,包括 SDF 、 APP ID 、 Gate Status 等,解析并存放到 UPF 全局变量中。 2、在用户面数据包处理时,查看数据包五元组是否能匹配上现有规则中的 SDF ,或者数据包访问的应用业务匹配上现有规则中的 APP ID ; 如果有匹配的,则依据上行或下行规则中的 Gate Status 字段进行操作,字段值为 1 则丢弃该数据包;字段为 0 则依据 FAR 规则进行转发。 ## 代码实现: 1 N4 接口 1.1 创建流程: 1) 解析 PDR - PDI - SDF Filter 以及 PDR - PDI - Ethernet Packet - SDF Filter 并将结果存储在 sess.pdr 中。 ```bash # file : upf_pfcp_api.c handle_create_pdr |_ vec_foreach (pdr, create_pdr) |_ if (ISSET_BIT (pdr->pdi.grp.fields, PDI_SDF_FILTER)) | |_ vec_foreach (sdf, pdr->pdi.sdf_filter) | |_ if (sdf->flags & F_SDF_FD) | |_ if (sdf->flags & F_SDF_TTC) | |_ if (sdf->flags & F_SDF_BID) |_ if (ISSET_BIT (pdr->pdi.grp.fields, PDI_ETHERNET_PACKET_FILTER)) |_ if (sdf) # 是否写漏了一个循环? |_ if (sdf->flags & F_SDF_FD) |_ if (sdf->flags & F_SDF_TTC) |_ if (sdf->flags & F_SDF_BID) ``` 2) 解析 PDR - PDI - Appilcation ID 并将结果存储在 sess.pdr 中。 ```bash # file : upf_pfcp_api.c handle_create_pdr |_ if (ISSET_BIT (pdr->pdi.grp.fields, PDI_APPLICATION_ID)) |_ hash_get_mem (gtm->hash_app_by_appname, pdr->pdi.application_id) ``` 3) 解析 QER - Gate Status 并将结果存储在 sess.rules.qer.gate_status[ UPF_UL ] 或 sess.rules.qer.gate_status[ UPF_DL ] 中。 ```bash # file : upf_pfcp_api.c handle_create_qer |_ vec_foreach (qer, create_qer) |_ create.gate_status[UPF_UL] = qer->gate_status.ul; |_ create.gate_status[UPF_DL] = qer->gate_status.dl; |_ if ((r = sx_create_qer (sess, &create)) != 0) ``` 1.2 修改流程: 1) 解析 PDR - PDI - SDF Filter 以及 PDR - PDI - Ethernet Packet - SDF Filter : ```bash # file : upf_pfcp_api.c handle_update_pdr |_ vec_foreach (pdr, update_pdr) |_ if (ISSET_BIT (pdr->pdi.grp.fields, PDI_SDF_FILTER)) |_ vec_foreach (pdr_sdf_filter, pdr->pdi.sdf_filter) |_ if (pdr_sdf_filter->flags & F_SDF_FD) |_ if (pdr_sdf_filter->flags & F_SDF_TTC) |_ if (pdr_sdf_filter->flags & F_SDF_BID) ``` 2) 解析 PDR - PDI - Application ID : ```bash # file : upf_pfcp_api.c handle_update_pdr |_ vec_foreach (pdr, update_pdr) |_ if (ISSET_BIT (pdr->pdi.grp.fields, PDI_APPLICATION_ID)) |_ hash_get_mem (gtm->hash_app_by_appname, pdr->pdi.application_id); ``` 3) 解析 QER - Gate Status : ```bash # file : upf_pfcp_api.c handle_update_qer |_ vec_foreach (qer, update_qer) |_ update = sx_get_qer (sess, SX_PENDING, qer->qer_id); |_ if (ISSET_BIT (qer->grp.fields, UPDATE_QER_GATE_STATUS)) |_ update->gate_status[UPF_UL] = qer->gate_status.ul; |_ update->gate_status[UPF_DL] = qer->gate_status.dl; ``` 2 用户数据面 1) 依据 gate_status 字段进行对报文的操作: ```bash # file : upf_process.c upf_process |_ while (n_left_from > 0) |_ while (n_left_from > 0 && n_left_to_next > 0) |_ direction = IS_DL (pdr, far) ? UPF_DL : UPF_UL; |_ if (process_qers (vm, sess, active, pdr, b, direction)) | \_ process_single_qer (sess, qer, b, direction, len) | |_ if (qer->gate_status[direction]) # close 就直接返回,否则继续 qer 处理逻辑。 | |_ return QER_DROP_CAUSE_GATE; # QER_DROP_CAUSE_GATE = 1 |_ next = UPF_PROCESS_NEXT_DROP; # 如果解析后需要丢弃,则下一节点是 DROP ,否则继续其他操作。 ``` 2) 处理转发参数 ```bash # file : upf_process.c upf_process |_ while (n_left_from > 0) |_ while (n_left_from > 0 && n_left_to_next > 0) |_ if (PREDICT_TRUE (upf_buffer_opaque (b)->upf.pdr_index < vec_len (active->pdr))) |_ pdr = active->pdr + upf_buffer_opaque (b)->upf.pdr_index; |_ far = sx_get_far_by_id (active, pdr->far_id); |_ if (PREDICT_TRUE (far->apply_action & FAR_FORWARD)) # 命中转发的规则 | |_ if (far->forward.flags & FAR_F_OUTER_HEADER_CREATION) # 添加外部数据包头 | |_ else if (far->forward.flags & FAR_F_REDIRECT_INFORMATION) # 重定向 | | |_ switch (far->forward.redirect_information.type) | | |_ case REDIRECT_INFORMATION_IPv4: | | | |_ next = UPF_PROCESS_NEXT_IP4_LOCAL; # 或 UPF_PROCESS_NEXT_IP4_LOOKUP | | |_ REDIRECT_INFORMATION_IPv6 | | |_ REDIRECT_INFORMATION_HTTP | |_ else | |_ if (upf_buffer_opaque (b)->upf.http && far->forward.header_enrichment && \ | | um->http_HE_config.state) | |_ if (upf_buffer_opaque (b)->upf.https_client_hello && \ | | far->forward.header_enrichment && um->https_HE_config.state) | |_ if (sess->pdn_type == PDN_TYPE_ETHERNET) | |_ else |_ else if (far->apply_action & FAR_BUFFER) # 命中缓存的规则 |_ else # DROP ```