查询ue标识信息命令行设计
mp2要求
mp2
要求mep
可调用相关接口,通过upf
查询ue
的标识信息,mep->upf会下发mepId
、ueIdType
和ueIdValue
,均为string
类型,ueIdType
可以为ipaddress
、supi
、gpsi
或者pei
,其中ipaddress
为ue
的原始ip
地址,也就是nat
前的ip
地址;upf->mep会返回查询到ue
的ipaddress
、supi
、gpsi
和pei
,同样为字符串类型。
upf支持的状态
目前,因为我们核心网的限制,upf
中只存储了ipaddress
和supi
信息,所以暂时只能支持通过ipaddress
和supi
去查询ue
的标识信息;由于mepId
对upf
来说没有意义,所以在upf
这边的命令行设计上,不下发该参数,该参数可在vpp agent
端存储。
代码流程图
命令行格式设计
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,
};
ueIdType
和ueIdValue
均为必选项,且是string
类型,根据规范,ueIdType
可以为ipaddress
、supi
、gpsi
和pei
,但目前代码实现并不支持通过gpsi
和pei
查询。
对下发参数的判定
判定有无参数下发
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);
}
}
判断是否下发了ueIdType
和ueIdValue
两个参数类型,若输入的不是此参数,则返回“unknown input”的报错;对ueIdType
具体下发的值,也要判断是否为ipaddress
、supi
、gpsi
和pei
中的一项,否则返回“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_flag
和ueidvalue_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
初始化时,已经创建了基于ipaddress
和supi
的hash
表(见下文说明),可通过这两张表查找到对应的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
转换后,作为此session
的key
,value
为sx - 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
进行了释放,同时将此session
从ipaddress
的hash
表中移除:
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表
与ipaddress
的hash
表操作类似,只是因为这里的key
为string
类型的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);
测试验证
环境说明
在管理ip
为172.18.22.216
的虚拟机上运行upf
服务,landslide
模拟ue
、smf
和基站
,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信息
使用
landslide
发送报文:查询
upf session
信息:可看到刚创建
session
的ue_ip
为70.70.70.1
,IMSI
(supi
)为466920100001101
。使用命令查询
ue
信息:可看到使用该命令可通过
ipaddress
或者supi
查询到对应的ue
信息。
测试用例2-session删除后查询ue信息
landslide
停止发送报文:查询
upf session
信息:可看到
session
已被删除。使用命令查询ue信息:
session
删除后,使用该命令不能再查到,返回查找失败的错误。
测试用例3-输入参数有误返回报错
输入参数为空:
参数类型不正确:
参数类型必须为
ueIdType
和ueIdValue
。输入的参数类型值不对:
ueIdType
的值必须为ipaddress
、supi
、gpsi
或者pei
。漏输参数:
ueIdType
和ueIdValue
缺一不可。输入了不支持的值:
暂不支持通过
gpsi
或者pei
查询ue
信息。