feat: 信令跟踪功能接口
This commit is contained in:
111
src/modules/trace/controller/packet.go
Normal file
111
src/modules/trace/controller/packet.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/utils/ctx"
|
||||
"be.ems/src/framework/vo/result"
|
||||
traceService "be.ems/src/modules/trace/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
// 实例化控制层 PacketController 结构体
|
||||
var NewPacket = &PacketController{
|
||||
packetService: traceService.NewPacket,
|
||||
}
|
||||
|
||||
// 信令跟踪
|
||||
//
|
||||
// PATH /trace/packet
|
||||
type PacketController struct {
|
||||
packetService *traceService.Packet // 信令跟踪服务
|
||||
}
|
||||
|
||||
// 网元跟踪网卡设备列表
|
||||
//
|
||||
// GET /devices
|
||||
func (s *PacketController) Devices(c *gin.Context) {
|
||||
data := s.packetService.NetworkDevices()
|
||||
c.JSON(200, result.OkData(data))
|
||||
}
|
||||
|
||||
// 网元跟踪开始
|
||||
//
|
||||
// POST /start
|
||||
func (s *PacketController) Start(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var body struct {
|
||||
Device string `json:"device" binding:"required"` // 网卡设备
|
||||
TaskNo string `json:"taskNo" binding:"required"` // 任务编号
|
||||
Output string `json:"output" ` // 输出PCAP文件路径,为空则不输出
|
||||
}
|
||||
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := s.packetService.LiveStart(body.TaskNo, body.Device, body.Output)
|
||||
if err != nil {
|
||||
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.OkData(msg))
|
||||
}
|
||||
|
||||
// 网元跟踪结束
|
||||
//
|
||||
// POST /stop
|
||||
func (s *PacketController) Stop(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var body struct {
|
||||
TaskNo string `json:"taskNo" binding:"required"` // 任务编号
|
||||
}
|
||||
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.packetService.LiveStop(body.TaskNo); err != nil {
|
||||
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.Ok(nil))
|
||||
}
|
||||
|
||||
// 网元跟踪过滤
|
||||
//
|
||||
// PUT /filter
|
||||
func (s *PacketController) Filter(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var body struct {
|
||||
TaskNo string `json:"taskNo" binding:"required"` // 任务编号
|
||||
Expr string `json:"expr" binding:"required"` // 过滤表达式(port 33030 or 33040)
|
||||
}
|
||||
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.packetService.LiveFilter(body.TaskNo, body.Expr); err != nil {
|
||||
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.Ok(nil))
|
||||
}
|
||||
|
||||
// 网元跟踪续期保活
|
||||
//
|
||||
// PUT /keep-alive
|
||||
func (s *PacketController) KeepAlive(c *gin.Context) {
|
||||
language := ctx.AcceptLanguage(c)
|
||||
var body struct {
|
||||
IntervalIn int `json:"intervalIn" ` // 服务续约的频率,默认设置为60秒
|
||||
Duration int `json:"duration" ` // 服务失效的时间,默认设置为120秒
|
||||
}
|
||||
err := c.ShouldBindBodyWith(&body, binding.JSON)
|
||||
if err != nil {
|
||||
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||
return
|
||||
}
|
||||
c.JSON(200, result.Ok(nil))
|
||||
}
|
||||
191
src/modules/trace/service/packet.go
Normal file
191
src/modules/trace/service/packet.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/logger"
|
||||
"be.ems/src/framework/vo"
|
||||
"github.com/gopacket/gopacket"
|
||||
"github.com/gopacket/gopacket/pcap"
|
||||
"github.com/gopacket/gopacket/pcapgo"
|
||||
)
|
||||
|
||||
// 实例化服务层 Packet 结构体
|
||||
var NewPacket = &Packet{
|
||||
taskMap: sync.Map{},
|
||||
}
|
||||
|
||||
// 信令跟踪 服务层处理
|
||||
type Packet struct {
|
||||
taskMap sync.Map // 捕获任务
|
||||
}
|
||||
|
||||
// task 任务信息
|
||||
type task struct {
|
||||
TaskNo string // 任务编号
|
||||
Handle *pcap.Handle // 捕获句柄
|
||||
File *os.File // 捕获信息输出文件句柄
|
||||
Writer *pcapgo.Writer // 捕获信息输出句柄
|
||||
Expire time.Time // 过期时间
|
||||
context context.Context // 上下文 控制完成结束
|
||||
}
|
||||
|
||||
// NetworkDevices 获取网卡设备信息
|
||||
func (s *Packet) NetworkDevices() []vo.TreeSelect {
|
||||
arr := make([]vo.TreeSelect, 0)
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
logger.Errorf("interfaces find all devices err: %s", err.Error())
|
||||
return arr
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
if len(device.Addresses) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lable := device.Description
|
||||
if lable == "" {
|
||||
lable = device.Name
|
||||
}
|
||||
item := vo.TreeSelect{
|
||||
ID: device.Name,
|
||||
Label: lable,
|
||||
Children: []vo.TreeSelect{},
|
||||
}
|
||||
|
||||
for _, address := range device.Addresses {
|
||||
if address.IP != nil {
|
||||
ip := address.IP.String()
|
||||
item.Children = append(item.Children, vo.TreeSelect{ID: ip, Label: ip})
|
||||
}
|
||||
}
|
||||
arr = append(arr, item)
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
// verifyDevice 检查网卡设备是否存在
|
||||
func (s *Packet) verifyDevice(str string) (string, bool) {
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
logger.Errorf("interfaces find all devices err: %s", err.Error())
|
||||
return "", false
|
||||
}
|
||||
for _, device := range devices {
|
||||
if len(device.Addresses) == 0 {
|
||||
continue
|
||||
}
|
||||
if device.Name == str {
|
||||
return device.Name, true
|
||||
}
|
||||
for _, address := range device.Addresses {
|
||||
if address.IP.String() == str {
|
||||
return device.Name, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// LiveStart 开始捕获数据
|
||||
func (s *Packet) LiveStart(taskNo, deviceName, outputFile string) (string, error) {
|
||||
if _, ok := s.taskMap.Load(taskNo); ok {
|
||||
return "", fmt.Errorf("task no. %s already exist", taskNo)
|
||||
}
|
||||
|
||||
// Verify the specified network interface exists
|
||||
device, deviceOk := s.verifyDevice(deviceName)
|
||||
if !deviceOk {
|
||||
return "", fmt.Errorf("network device not exist: %s", deviceName)
|
||||
}
|
||||
|
||||
snapshotLength := 262144
|
||||
|
||||
// open device
|
||||
handle, err := pcap.OpenLive(device, int32(snapshotLength), true, pcap.BlockForever)
|
||||
if err != nil {
|
||||
logger.Errorf("open live err: %s", err.Error())
|
||||
if strings.Contains(err.Error(), "operation not permitted") {
|
||||
return "", fmt.Errorf("you don't have permission to capture on that/these device(s)")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// write a new file
|
||||
var w *pcapgo.Writer
|
||||
var f *os.File
|
||||
if outputFile != "" {
|
||||
f, err = os.Create(outputFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
w = pcapgo.NewWriter(f)
|
||||
w.WriteFileHeader(uint32(snapshotLength), handle.LinkType())
|
||||
}
|
||||
|
||||
// capture packets
|
||||
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
|
||||
packetSource.Lazy = false
|
||||
packetSource.NoCopy = true
|
||||
packetSource.DecodeStreamsAsDatagrams = true
|
||||
|
||||
// save tasks
|
||||
ctx := context.Background()
|
||||
task := &task{
|
||||
TaskNo: taskNo,
|
||||
Handle: handle,
|
||||
File: f,
|
||||
Writer: w,
|
||||
Expire: time.Now().Add(time.Second * 120),
|
||||
context: ctx,
|
||||
}
|
||||
s.taskMap.Store(taskNo, task)
|
||||
|
||||
// start capture
|
||||
go func() {
|
||||
for packet := range packetSource.PacketsCtx(ctx) {
|
||||
if packet == nil {
|
||||
continue
|
||||
}
|
||||
if packet.Metadata().Timestamp.Before(time.Now()) {
|
||||
continue
|
||||
}
|
||||
if w != nil {
|
||||
w.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
|
||||
}
|
||||
fmt.Println(packet.Dump())
|
||||
}
|
||||
}()
|
||||
return "task initiated", nil
|
||||
}
|
||||
|
||||
// LiveFilter 捕获过滤
|
||||
func (s *Packet) LiveFilter(taskNo, expr string) error {
|
||||
info, ok := s.taskMap.Load(taskNo)
|
||||
if !ok {
|
||||
return fmt.Errorf("task no. %s not exist", taskNo)
|
||||
}
|
||||
return info.(*task).Handle.SetBPFFilter(expr)
|
||||
}
|
||||
|
||||
// LiveStop 停止捕获数据
|
||||
func (s *Packet) LiveStop(taskNo string) error {
|
||||
info, ok := s.taskMap.Load(taskNo)
|
||||
if !ok {
|
||||
return fmt.Errorf("task no. %s not exist", taskNo)
|
||||
}
|
||||
info.(*task).context.Done()
|
||||
info.(*task).Handle.Close()
|
||||
if info.(task).File != nil {
|
||||
info.(task).File.Close()
|
||||
}
|
||||
s.taskMap.Delete(taskNo)
|
||||
return nil
|
||||
}
|
||||
@@ -41,6 +41,35 @@ func Setup(router *gin.Engine) {
|
||||
)
|
||||
}
|
||||
|
||||
// 信令跟踪
|
||||
packetGroup := router.Group("/trace/packet")
|
||||
{
|
||||
packetGroup.GET("/devices",
|
||||
middleware.PreAuthorize(nil),
|
||||
controller.NewPacket.Devices,
|
||||
)
|
||||
packetGroup.POST("/start",
|
||||
middleware.PreAuthorize(nil),
|
||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.packet", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||
controller.NewPacket.Start,
|
||||
)
|
||||
packetGroup.POST("/stop",
|
||||
middleware.PreAuthorize(nil),
|
||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.packet", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||
controller.NewPacket.Stop,
|
||||
)
|
||||
packetGroup.PUT("/filter",
|
||||
middleware.PreAuthorize(nil),
|
||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.packet", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||
controller.NewPacket.Filter,
|
||||
)
|
||||
packetGroup.PUT("/keep-alive",
|
||||
middleware.PreAuthorize(nil),
|
||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.packet", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||
controller.NewPacket.KeepAlive,
|
||||
)
|
||||
}
|
||||
|
||||
// 跟踪任务 网元HLR (免登录)
|
||||
taskHLRGroup := router.Group("/trace/task/hlr")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user