## VPP-Agent MP2 MP2 接口的开发通过 VPP-Agent 进行开发,使用的是 UPF 通过 Watch VPP-Agent 的 KV 数据库来进行管理的。 ### 架构 分两种接口,大多数通过 restful API 下发到数据库,然后 watch 数据库变化通过 binary api 下发给 UPF;第二种查询类接口,如 UE INFO 和 UE 位置信息直接通过 binary api 下发给 UPF。 ![mp2 vpp-agent arch](../../../../_static/vpp_agent_mp2_architecture.png) ### VPP-Agent 北向接口 VPP-Agent 原生支持 Restful 的方法。通过使用 gorilla/mux 框架提供的基础 http 服务 那么,我们需要为 UPF Plugin 加 restful 的注册。 在 UPF Plugin 注册时为其添加 HTTP Handler plugins/vpp/upfplugin/upfplugin.go ``` type Deps struct { ...... HTTPHandlers rest.HTTPHandlers } func (p *UpfPlugin) AfterInit() error { ...... p.registerHandlers(p.HTTPHandlers) /// 注册对应 url 的方法 } ``` plugins/vpp/upfplugin/options.go ``` func NewPlugin(opts ...Option) *UpfPlugin { ...... p.HTTPHandlers = &rest.DefaultPlugin ...... } ``` ### VPP-Agent 注册 URL /plugins/vpp/upfplugin/ 下新建文件 rest_api.go 利用 http.RegisterHTTPHandler 注册 URL ,依次是 url,执行方法,CRUD。 ``` func (p *UpfPlugin) registerHandlers(http rest.HTTPHandlers) { // Register mp2 traffic_rule http.RegisterHTTPHandler(mp2 + traffic_rule, p.mp2GetTrafficRuleHandler, "GET") // Register ue info http.RegisterHTTPHandler(mp2 + "/ueinformation/query/ue_id" } ``` 然后在定义方法即可,所有接口暂定写在此文件 根据方法中的调度,可以选择通过 p.RedisBroker 读写数据库,也可以通过 p.upfHandler 直接调用 UPF API ### VPP-Agent 所调用的 UPF API UPF 编译安装后在 /usr/share/vpp/api/plugins 提供 upf.api.json 文件,可以通过 binapi-genreate 命令生成 接口使用代码 upf.ba.go,里面定义了调接口时 request 和 reply 的结构体。 如 UE Information 的结构体 ``` type UpfUeInformation struct { UeidType string `binapi:"string[32],name=ueid_type" json:"ueid_type,omitempty"` UeidValue string `binapi:"string[32],name=ueid_value" json:"ueid_value,omitempty"` } ``` 在调用时,通过此结构体初始化 ### VPP-Agent 调用处理后数据 北向 Restful API 收到数据后,根据之前 upf.ba.go 结构体创建 upf model 部分代码,需要先修改 proto/ligato/vpp/upf/pfcp.proto ``` message UpfUeInformation { bytes UeidType = 1; bytes UeidValue = 2; } ``` 然后通过 make generate-proto 命令生成 pfcp.pb.go ### VPP-Agent 的 Call 操作 修改 /plugins/vpp/upfplugin/vppcalls/vpp2009/upf_vppcalls.go 添加具体的发送方法,将 pfcp.pb.go 中数据复制给 upf.ba.go 里的结构体中,然后发送给 UPF ``` func (h *UpfVppHandler) VppGetUeInfo(entry *models.UpfUeInformation) (*vppcalls.UeInformationRep, error) { ...... request := &upf.UpfUeInformation{} reply := &upf.UpfUeInformationReply{} Copy(request, entry) err = h.callsChannel.SendRequest(request).ReceiveReply(reply) } ``` 然后将该方法 注册到 type UpfPFCPVppAPI 中 修改 /plugins/vpp/upfplugin/vppcalls/upf_vppcalls.go ``` type UpfPFCPVppAPI interface { ...... VppGetUeInfo(entry *upf.UpfUeInformation) (*UeInformationRep, error) } ``` ### UE 信息查询实现 #### 需求 通过 MP2 接口输入 过滤的type 和具体数据进行 UE 的信息查询 具体方法:https://{ip}:{port}/mp2/v1/meps/{mepId}/ueinformation/query/ue_id?type={ueIdType}&value={ueIdValue} 需要解析 type 和 value 的值 #### 北向 Restful /plugins/vpp/upfplugin/rest_api.go ``` func (p *UpfPlugin) registerHandlers(http rest.HTTPHandlers) { http.RegisterHTTPHandler(mp2 + ueInfo, p.mp2GetUeInfoHandler, "GET") } ``` 通过 args := req.URL.Query() 获取到具体参数,然后通过 entry := &models.UpfUeInformation{ UeidType: []byte(ueIdType), UeidValue: []byte(ueIdValue), } 组装具体请求结构体,进行请求,返回 ueInfo ``` ueInfo, err := p.upfHandler.VppGetUeInfo(entry) ``` #### 返回结果 如果查询不到,返回对应查询错误,目前情况只报 ``` "500 Internal server error: sending request failed: VppGetUeInfo error VPPApiError: Unspecified Error (-1)\n" ``` 如果查询到,则返回 200 和 正确结果 ``` # curl -X GET "http://127.0.0.1:9191/mp2/v1/meps/123/ueinformation/query/ue_id?type=ipaddress&value=172.20.231.1" { "address": "172.20.231.1", "supi": "466920100001101" } ```