查询ue标识信息命令行设计

mp2要求

mp2要求mep可调用相关接口,通过upf查询ue的标识信息,mep->upf会下发mepIdueIdTypeueIdValue,均为string类型,ueIdType可以为ipaddresssupigpsi或者pei,其中ipaddressue的原始ip地址,也就是nat前的ip地址;upf->mep会返回查询到ueipaddresssupigpsipei,同样为字符串类型。

upf支持的状态

目前,因为我们核心网的限制,upf中只存储了ipaddresssupi信息,所以暂时只能支持通过ipaddresssupi去查询ue的标识信息;由于mepIdupf来说没有意义,所以在upf这边的命令行设计上,不下发该参数,该参数可在vpp agent端存储。

代码流程图

ue_information_0

命令行格式设计

VLIB_CLI_COMMAND(show_ue_command, static) = {
    .path = "show upf ue_information",
    .short_help = "show upf ue_information ueIdType <ipaddress|supi|gpsi|pei> ueIdValue <value>",
    .function = show_ue_command_fn,
};

ueIdTypeueIdValue均为必选项,且是string类型,根据规范,ueIdType可以为ipaddresssupigpsipei,但目前代码实现并不支持通过gpsipei查询。

对下发参数的判定

判定有无参数下发

if (!unformat_user(input, unformat_line_input, line_input))
{
    return clib_error_return(0, "no parameter",
                             format_unformat_error);
}

在程序最开始先判断下发参数是否为空,若为空则返回“no parameter”的报错。

判断下发参数是否合法

while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT)
{
    if (unformat(line_input, "ueIdType %s", &ueidtype))
    {
        for (i = IPADDRESS; i < MAX_TYPE_NUM; i++)
        {
            if (0 == strncmp(ue_type_collection[i], (const char *)ueidtype, strlen((const char *)ueidtype)))
            {
                ueidtype_flag = i;
                break;
            }
        }
        if (0 == ueidtype_flag)
        {
            return clib_error_return(0, "ueIdType error",
                                     format_unformat_error);
        }
    }
    else if (unformat(line_input, "ueIdValue %s", &ueidvalue))
    {
        ueidvalue_flag = 1;
    }
    else
    {
        return clib_error_return(0, "unknown input '%U'",
                                 format_unformat_error, line_input);
    }
}

判断是否下发了ueIdTypeueIdValue两个参数类型,若输入的不是此参数,则返回“unknown input”的报错;对ueIdType具体下发的值,也要判断是否为ipaddresssupigpsipei中的一项,否则返回“ueIdType error”的报错;若下发了ueIdType参数,则ueidtype_flag置位为具体的下发类型,若下发了ueIdValue参数,则ueidvalue_flag置位为1。

if (ueidtype_flag && ueidvalue_flag)
{
    if (IPADDRESS == ueidtype_flag)
    {
        ...
    }
    else if (SUPI == ueidtype_flag)
    {
        ...
    }
    else if (GPSI == ueidtype_flag)
    {
        return clib_error_return(0, "not support gpsi",
                                 format_unformat_error);
    }
    else if (PEI == ueidtype_flag)
    {
        return clib_error_return(0, "not support pei",
                                 format_unformat_error);
    }
}
else
{
    return clib_error_return(0, "missing parameter",
                             format_unformat_error);
}

只有ueidtype_flagueidvalue_flag同时置位,才进行后续的查找ue信息流程,否则返回“missing parameter”的错误;若ueidtype_flag置位为GPSI或者PEI,则返回不支持此类型的错误。

通过ipaddress或者supi查询ue信息

if (IPADDRESS == ueidtype_flag)
{
    if (ipv4_to_u32(ueidvalue, &ipv4_addr)) /* 将传入ip由字符串类型转化为u32类型 */
    {
        session = sx_lookup_by_ue_ipv4(ipv4_addr); /* 通过ipaddress查找session */
    }
}
else if (SUPI == ueidtype_flag)
{
    session = sx_lookup_by_imsi((u8 *)ueidvalue); /* 通过supi查找session */
}

upf初始化时,已经创建了基于ipaddresssupihash表(见下文说明),可通过这两张表查找到对应的upf session,我们需要的信息都在upf session结构体中,将需要的信息拷贝出打印即可。

if (session)
{
    if (ip46_address_is_ip4 (&session->ue_address))
    {
        snprintf(ue.ipaddress, MAX_IP_LEN, "%hhu.%hhu.%hhu.%hhu",
                 session->ue_address.ip4.data[0], session->ue_address.ip4.data[1],
                 session->ue_address.ip4.data[2], session->ue_address.ip4.data[3]);
    }
    else
    {
        return clib_error_return(0, "not support ipv6",
                                 format_unformat_error);
    }
    strncpy(ue.supi, (const char *)session->user_id.imsi_str, strlen((const char        *)session->user_id.imsi_str));
    strncpy(ue.gpsi, (const char *)session->user_id.msisdn, session->user_id.msisdn_len);
    strncpy(ue.pei, (const char *)session->user_id.imei, session->user_id.imei_len);
    vlib_cli_output (vm, "ipaddress = %s supi = %s gpsi = %s pei = %s\n", ue.ipaddress, ue.supi, ue.gpsi, ue.pei);
}
else
{
    return clib_error_return(0, "lookup error",
                             format_unformat_error);
}

若查找到session,将需要的信息拷贝到struct ue_information ue这个变量中,并打印出来;若找不到对应的session,则返回“lookup error”的报错。

ipaddress的hash表

hash的创建

upf_init函数中创建:

sm->session_by_ue_ipv4 = hash_create(0, sizeof(uword));

因为此处hash的key为u32类型的ip地址,所以选用hash_create函数,第一个参数为哈希桶的长度,第二个参数为存储value的字节数,设置为sizeof(uword),代码中uword为u32的别名,所以这里设置为四字节。

节点的添加

build_sx_rules函数中添加节点:

if (pdr->pdi.ue_addr.flags & IE_UE_IP_ADDRESS_V4)
{
    sx->ue_address.ip4.as_u32 = pdr->pdi.ue_addr.ip4.as_u32;
    hash_set(gtm->session_by_ue_ipv4, ntohl(sx->ue_address.ip4.as_u32),
             sx - gtm->sessions);
}

获取了ue ip的地址,经过ntohl转换后,作为此sessionkeyvaluesx - gtm->sessions,指向当前的session

节点的查找

sx_lookup_by_ue_ipv4 (u32 ue_ipv4)
{
    upf_main_t *gtm = &upf_main;
    uword *p = NULL;

    upf_debug("ue_ipv4 = %u\n", ue_ipv4);
    p = hash_get(gtm->session_by_ue_ipv4, ue_ipv4);

    if (!p)
      return NULL;
    else
      return pool_elt_at_index (gtm->sessions, p[0]);

}

传入u32类型的ip值作为key去查找,调用hash_get获取对应的session

节点的删除

sx_disable_session函数中,对session进行了释放,同时将此sessionipaddresshash表中移除:

if (sx->ue_address.ip4.as_u32)
{
    hash_unset(gtm->session_by_ue_ipv4, ntohl(sx->ue_address.ip4.as_u32));
}

调用hash_unset函数删除该节点。

supi的hash表

ipaddresshash表操作类似,只是因为这里的keystring类型的supi,所以使用接口不一样。

hash的创建

upf_init函数中创建:

sm->session_by_imsi = hash_create_string (0, sizeof (uword));

节点的添加

handle_session_establishment_request函数中添加节点:

hash_set_mem (gtm->session_by_imsi, sess->user_id.imsi_str,
                           sess - gtm->sessions);

节点的查找

sx_lookup_by_imsi函数中查找节点:

p = hash_get_mem (gtm->session_by_imsi, imsi);

节点的删除

sx_disable_session函数中,删除节点:

hash_unset_mem (gtm->session_by_imsi, sx->user_id.imsi_str);

测试验证

环境说明

在管理ip172.18.22.216的虚拟机上运行upf服务,landslide模拟uesmf基站upf的配置文件如下:

startup.conf:

heapsize 2G
unix {
    # nodaemon
    cli-listen /run/vpp/cli.sock
    log /var/log/vpp/vpp.log
    full-coredump
    gid vpp
    exec /etc/vpp/upf.conf
}
api-trace {
    on
}
logging {
  default-syslog-log-level info
  unthrottle-time 1
}
api-segment {
  gid vpp
}

upf {
  white-list-conf /etc/vpp/white_list_conf.xml
  grey-list-conf /etc/vpp/grey_list_conf.xml
  http-header-enrichment-conf /etc/vpp/http_header_enrichment_conf.xml
  https-header-enrichment-conf /etc/vpp/https_header_enrichment_conf.xml
  # predef-conf /etc/vpp/predef_conf.xml
}

buffers {
  buffers-per-numa 32768
}
statseg {
  size 256m
}
socksvr {
    default
}
cpu {
        main-core 0
        #corelist-pfcp-threads 1
        #corelist-workers 2
        workers 2
        pfcp-threads 1
}
dpdk {
    dev 0000:00:03.0 {
              num-rx-queues 1
    }
    dev 0000:00:0a.0 {
              num-rx-queues 1
    }
    dev 0000:00:0b.0 {
              num-rx-queues 1
    }
}
plugins {
    plugin gtpu_plugin.so { disable }
    plugin ndpi_plugin.so { disable }
}

dns {
    max-ttl 120
}
upf.conf:

upf enable-disable
upf log level debug
upf feature FTUP support


set ip6 address TenGigabitEthernet0/a/0 2000:200:600::1/64

ip6 nd TenGigabitEthernet0/a/0 ra-suppress

set int state TenGigabitEthernet0/3/0 up


set int state TenGigabitEthernet0/a/0 up

set int state GigabitEthernet0/b/0 up

set interface mtu ip4 1500 TenGigabitEthernet0/3/0
set interface mtu ip4 9000 GigabitEthernet0/b/0
set interface mtu ip6 9000 TenGigabitEthernet0/a/0

set interface reassembly TenGigabitEthernet0/3/0 on
set interface reassembly TenGigabitEthernet0/a/0 on
set interface reassembly GigabitEthernet0/b/0 on


set int ip address GigabitEthernet0/b/0 192.168.4.100/24
set int ip address TenGigabitEthernet0/3/0 192.168.3.100/24
set int ip address TenGigabitEthernet0/a/0 192.168.6.100/24

ip route add 0.0.0.0/0 table 0 via 192.168.6.110 TenGigabitEthernet0/a/0


ip route add ::/0 table 0 via 2000:200:600::75 TenGigabitEthernet0/a/0

upf pfcp endpoint ip 192.168.4.100 vrf 0



upf nwi name epc vrf 0

upf gtpu endpoint ip 192.168.4.100 intf cp nwi epc TEID-Range-Indication 4
upf gtpu endpoint ip 192.168.3.100 intf access nwi epc TEID-Range-Indication 4


upf pfd-list appid 111 fd {permit out 17 from any 3333 to 70.70.70.1/24 2222}
upf pfd-list appid 112 fd {permit out 17 from any 5555 to 70.70.70.1/24 4444}
upf pfd-list appid 110 url {www.baidu.com}


upf pre-def rule rule_name activf app_id 111
upf dnn name default.mnc092.mcc466.gprs rule_id activf

upf pre-def rule rule_name active app_id 112
upf dnn name default.mnc092.mcc466.gprs rule_id active

测试用例1-session创建后查询ue信息

  1. 使用landslide发送报文:

    ue_information_1

  2. 查询upf session信息:

    ue_information_2

    ue_information_3

    可看到刚创建sessionue_ip70.70.70.1IMSIsupi)为466920100001101

  3. 使用命令查询ue信息:

    ue_information_4

    可看到使用该命令可通过ipaddress或者supi查询到对应的ue信息。

测试用例2-session删除后查询ue信息

  1. landslide停止发送报文:

    ue_information_5

  2. 查询upf session信息:

    ue_information_6

    可看到session已被删除。

  3. 使用命令查询ue信息:

    ue_information_7

    session删除后,使用该命令不能再查到,返回查找失败的错误。

测试用例3-输入参数有误返回报错

  1. 输入参数为空:

    ue_information_8

  2. 参数类型不正确:

    ue_information_9

    参数类型必须为ueIdTypeueIdValue

  3. 输入的参数类型值不对:

    ue_information_10

    ueIdType的值必须为ipaddresssupigpsi或者pei

  4. 漏输参数:

    ue_information_11

    ueIdTypeueIdValue缺一不可。

  5. 输入了不支持的值:

    ue_information_12

    暂不支持通过gpsi或者pei查询ue信息。