feat: 跟踪抓包重构

This commit is contained in:
TsMask
2024-08-31 14:25:54 +08:00
parent a8e976039c
commit d3187e86ec
4 changed files with 225 additions and 155 deletions

View File

@@ -1,9 +1,13 @@
package controller package controller
import ( import (
"os"
"path/filepath"
"nms_cxy/src/framework/i18n" "nms_cxy/src/framework/i18n"
"nms_cxy/src/framework/utils/ctx" "nms_cxy/src/framework/utils/ctx"
"nms_cxy/src/framework/vo/result" "nms_cxy/src/framework/vo/result"
neService "nms_cxy/src/modules/network_element/service"
traceService "nms_cxy/src/modules/trace/service" traceService "nms_cxy/src/modules/trace/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -13,6 +17,7 @@ import (
// 实例化控制层 TcpdumpController 结构体 // 实例化控制层 TcpdumpController 结构体
var NewTcpdump = &TcpdumpController{ var NewTcpdump = &TcpdumpController{
TcpdumpService: traceService.NewTcpdumpImpl, TcpdumpService: traceService.NewTcpdumpImpl,
neInfoService: neService.NewNeInfoImpl,
} }
// 信令抓包请求 // 信令抓包请求
@@ -21,6 +26,8 @@ var NewTcpdump = &TcpdumpController{
type TcpdumpController struct { type TcpdumpController struct {
// 信令抓包服务 // 信令抓包服务
TcpdumpService traceService.ITcpdump TcpdumpService traceService.ITcpdump
// 网元信息服务
neInfoService neService.INeInfo
} }
// 网元抓包PACP 开始 // 网元抓包PACP 开始
@@ -31,7 +38,7 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
var body struct { var body struct {
NeType string `json:"neType" binding:"required"` // 网元类型 NeType string `json:"neType" binding:"required"` // 网元类型
NeId string `json:"neId" binding:"required"` // 网元ID NeId string `json:"neId" binding:"required"` // 网元ID
Cmd string `json:"cmd" binding:"required"` // 命令 "-n -s 0 -v -w" Cmd string `json:"cmd" binding:"required"` // 命令 "-n -s 0 -v"
} }
err := c.ShouldBindBodyWith(&body, binding.JSON) err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil { if err != nil {
@@ -39,21 +46,12 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
return return
} }
fileName, err := s.TcpdumpService.DumpStart(body.NeType, body.NeId, body.Cmd) taskCode, err := s.TcpdumpService.DumpStart(body.NeType, body.NeId, body.Cmd)
if err != nil { if err != nil {
msg := err.Error() c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
if msg == "noData" {
// 找不到 %s %s 对应网元信息
msg = i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
}
c.JSON(200, result.ErrMsg(msg))
return return
} }
c.JSON(200, result.OkData(map[string]any{ c.JSON(200, result.OkData(taskCode))
"msg": "tcpdump started",
"out": fileName,
"log": "",
}))
} }
// 网元抓包PACP 结束 // 网元抓包PACP 结束
@@ -62,9 +60,9 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
func (s *TcpdumpController) DumpStop(c *gin.Context) { func (s *TcpdumpController) DumpStop(c *gin.Context) {
language := ctx.AcceptLanguage(c) language := ctx.AcceptLanguage(c)
var body struct { var body struct {
NeType string `json:"neType" binding:"required"` // 网元类型 NeType string `json:"neType" binding:"required"` // 网元类型
NeId string `json:"neId" binding:"required"` // 网元ID NeId string `json:"neId" binding:"required"` // 网元ID
FileName string `json:"fileName"` // 文件名 查看日志信息 TaskCode string `json:"taskCode" binding:"required"` // 任务码,停止任务并查看日志信息
} }
err := c.ShouldBindBodyWith(&body, binding.JSON) err := c.ShouldBindBodyWith(&body, binding.JSON)
if err != nil { if err != nil {
@@ -72,29 +70,48 @@ func (s *TcpdumpController) DumpStop(c *gin.Context) {
return return
} }
logMsg, err := s.TcpdumpService.DumpStop(body.NeType, body.NeId, body.FileName) taskLog, err := s.TcpdumpService.DumpStop(body.NeType, body.NeId, body.TaskCode)
if err != nil { if err != nil {
msg := err.Error() c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
if msg == "noData" {
// 找不到 %s %s 对应网元信息
msg := i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
c.JSON(200, result.ErrMsg(msg))
return
}
c.JSON(200, result.ErrMsg(msg))
return return
} }
c.JSON(200, result.OkData(map[string]any{ c.JSON(200, result.OkMsg(taskLog))
"msg": "tcpdump stopped", }
"out": body.FileName,
"log": logMsg, // 网元抓包PACP 下载
})) //
// GET /download
func (s *TcpdumpController) DumpDownload(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
NeType string `form:"neType" binding:"required"` // 网元类型
NeID string `form:"neId" binding:"required"` // 网元ID
TaskCode string `form:"taskCode" binding:"required"` // 任务码,停止任务并查看日志信息
DelTemp bool `form:"delTemp"` // 完成后是否删除本地临时zip文件
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
zipFilePath, err := s.TcpdumpService.DumpDownload(query.NeType, query.NeID, query.TaskCode)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}
defer func() {
if query.DelTemp {
_ = os.Remove(zipFilePath)
}
}()
c.FileAttachment(zipFilePath, filepath.Base(zipFilePath))
} }
// UPF标准版内部抓包 // UPF标准版内部抓包
// //
// POST /traceUPF // POST /upf
func (s *TcpdumpController) TraceUPF(c *gin.Context) { func (s *TcpdumpController) UPFTrace(c *gin.Context) {
language := ctx.AcceptLanguage(c) language := ctx.AcceptLanguage(c)
var body struct { var body struct {
NeType string `json:"neType" binding:"required"` // 网元类型 NeType string `json:"neType" binding:"required"` // 网元类型
@@ -107,19 +124,10 @@ func (s *TcpdumpController) TraceUPF(c *gin.Context) {
return return
} }
fileName, logMsg, err := s.TcpdumpService.DumpUPF(body.NeType, body.NeId, body.Cmd) msg, err := s.TcpdumpService.UPFTrace(body.NeType, body.NeId, body.Cmd)
if err != nil { if err != nil {
msg := err.Error() c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
if msg == "noData" {
// 找不到 %s %s 对应网元信息
msg = i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
}
c.JSON(200, result.ErrMsg(msg))
return return
} }
c.JSON(200, result.OkData(map[string]any{ c.JSON(200, result.OkData(msg))
"msg": "trace UPF dump pacp",
"out": fileName,
"log": logMsg,
}))
} }

View File

@@ -2,12 +2,15 @@ package service
// 信令抓包 服务层接口 // 信令抓包 服务层接口
type ITcpdump interface { type ITcpdump interface {
// DumpStart 触发tcpdump开始抓包 filePcapName, err // DumpStart 触发tcpdump开始抓包
DumpStart(neType, neId, cmdStr string) (string, error) DumpStart(neType, neId, cmdStr string) (string, error)
// DumpStop 停止已存在抓包句柄 // DumpStop 停止已存在抓包句柄
DumpStop(neType, neId, fileName string) (string, error) DumpStop(neType, neId, taskCode string) (string, error)
// DumpUPF UPF标准版抓包 // DumpDownload 抓包文件网元端复制到本地输出zip文件
DumpUPF(neType, neId, cmdStr string) (string, string, error) DumpDownload(neType, neId, taskCode string) (string, error)
// UPFTrace UPF标准版内部抓包
UPFTrace(neType, neId, cmdStr string) (string, error)
} }

View File

@@ -2,36 +2,45 @@ package service
import ( import (
"fmt" "fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"strings" "strings"
"sync"
"time" "time"
"nms_cxy/src/framework/logger" "nms_cxy/src/framework/logger"
"nms_cxy/src/framework/utils/date" "nms_cxy/src/framework/utils/file"
neService "nms_cxy/src/modules/network_element/service" neService "nms_cxy/src/modules/network_element/service"
) )
// 实例化服务层 TcpdumpImpl 结构体 // 实例化服务层 TcpdumpImpl 结构体
var NewTcpdumpImpl = &TcpdumpImpl{ var NewTcpdumpImpl = &TcpdumpImpl{
neInfoService: neService.NewNeInfoImpl, neInfoService: neService.NewNeInfoImpl,
tcpdumpPIDMap: map[string]string{},
} }
// 信令抓包 服务层处理 // 信令抓包 服务层处理
type TcpdumpImpl struct { type TcpdumpImpl struct {
// 网元信息服务 // 网元信息服务
neInfoService neService.INeInfo neInfoService neService.INeInfo
// 抓包进程PID
tcpdumpPIDMap map[string]string
} }
// DumpStart 触发tcpdump开始抓包 filePcapName, err // 抓包进程PID
var dumpPIDMap sync.Map
// DumpStart 触发tcpdump开始抓包
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) { func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "w") {
return "", fmt.Errorf("command cannot contain -w")
}
// 查询网元获取IP // 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" { if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("noData") return "", fmt.Errorf("app.common.noNEInfo")
} }
// 网元主机的SSH客户端 // 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId) sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil { if err != nil {
@@ -39,47 +48,53 @@ func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
} }
defer sshClient.Close() defer sshClient.Close()
// 是否拥有sudo权限并拼接 // 检查是否安装tcpdump
withSudo := "" if msg, err := sshClient.RunCMD("sudo tcpdump --version"); err != nil {
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil { // bash: tcpdump: command not found
withSudo = "sudo "
}
if msg, err := sshClient.RunCMD(fmt.Sprintf("%s tcpdump --version", withSudo)); err != nil {
// stderr: bash: tcpdump未找到命令 => exit status 127
msg = strings.TrimSpace(msg) msg = strings.TrimSpace(msg)
logger.Warnf("DumpStart err: %s => %s", msg, err.Error()) logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
return "", fmt.Errorf(msg) return "", fmt.Errorf(msg)
} }
// 拼装命令 taskCode := time.Now().Format("20060102150405")
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) // 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS) neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID) sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo chmod 777 -R /tmp/omc", neDirTemp))
sendCmd := fmt.Sprintf("cd /tmp \n %s nohup timeout 30m tcpdump -i any %s -s0 -w %s.pcap > %s.log 2>&1 & \necho $!", withSudo, cmdStr, fileName, fileName)
// cd /tmp // 命令拼装
// sudo nohup timeout 60m tcpdump -i any -n -s 0 -v -w -s0 -w 20240115140822_UDM_001.pcap > 20240115140822_UDM_001.log 2>&1 & echo $! logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
msg, err := sshClient.RunCMD(sendCmd) filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
msg = strings.TrimSpace(msg) if strings.Contains(cmdStr, "-G") {
if err != nil || strings.HasPrefix(msg, "stderr:") { filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
logger.Warnf("DumpStart err: %s => %s", msg, err.Error()) }
sendCmd := fmt.Sprintf("sudo timeout 60m sudo tcpdump -i any %s -w %s > %s 2>&1 & echo $!", cmdStr, filePath, logPath)
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -G 60 -W 6 -w /tmp/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap > /tmp/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /tmp/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap > /tmp/omc/tcpdump/udm/001/20240817105440/tcpdump.log 2>&1 & echo $!
//
// timeout 超时60分钟后发送kill命令1分钟后强制终止命令。tcpdump -G 文件轮转间隔时间(秒) -W 文件轮转保留最近数量
// sudo timeout --kill-after=1m 60m sudo tcpdump -i any -n -s 0 -v -G 10 -W 7 -w /tmp/part_%Y%m%d%H%M%S.pcap > /tmp/part.log 2>&1 & echo $!
// sudo kill $(pgrep -P 722729)
outputPID, err := sshClient.RunCMD(sendCmd)
outputPID = strings.TrimSpace(outputPID)
if err != nil || strings.HasPrefix(outputPID, "stderr:") {
logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
return "", err return "", err
} }
// 检查进程 ps aux | grep tcpdump // 检查进程 ps aux | grep tcpdump
// 强杀 sudo pkill tcpdump // 强杀 sudo pkill tcpdump
s.tcpdumpPIDMap[neTypeID] = msg pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
return fileName, err dumpPIDMap.Store(pidKey, outputPID)
return taskCode, err
} }
// DumpStop 停止已存在抓包句柄 // DumpStop 停止已存在抓包句柄
func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) { func (s *TcpdumpImpl) DumpStop(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP // 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" { if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("noData") return "", fmt.Errorf("app.common.noNEInfo")
} }
// 网元主机的SSH客户端 // 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId) sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil { if err != nil {
@@ -87,107 +102,147 @@ func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) {
} }
defer sshClient.Close() defer sshClient.Close()
// 是否拥有sudo权限并拼接 // 是否存在执行过的进程
withSudo := "" pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil { pid, ok := dumpPIDMap.Load(pidKey)
withSudo = "sudo "
}
// 是否存在进程
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
pid, ok := s.tcpdumpPIDMap[neTypeID]
if !ok || pid == "" { if !ok || pid == "" {
return "", fmt.Errorf("tcpdump is not running") return "", fmt.Errorf("tcpdump is not running")
} }
defer dumpPIDMap.Delete(pidKey)
// 查看日志 // 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
viewLogFile := "" neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
if fileName != "" && strings.Contains(fileName, neTypeID) { // 命令拼装
viewLogFile = fmt.Sprintf("\n cat %s.log", fileName) sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s cat %s/tcpdump.log", pid, neDirTemp)
} // pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s cat tcpdump.log
output, err := sshClient.RunCMD(sendCmd)
// 拼装命令 if err != nil || strings.HasPrefix(output, "stderr:") {
sendCmd := fmt.Sprintf("cd /tmp \n %s kill %s %s", withSudo, pid, viewLogFile) logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(output), err.Error())
msg, err := sshClient.RunCMD(sendCmd)
delete(s.tcpdumpPIDMap, neTypeID)
if err != nil || strings.HasPrefix(msg, "stderr:") {
logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(msg), err.Error())
return "", err return "", err
} }
return msg, nil return output, nil
} }
// DumpUPF UPF标准版抓包 // DumpDownload 抓包文件网元端复制到本地输出zip文件
func (s *TcpdumpImpl) DumpUPF(neType, neId, cmdStr string) (string, string, error) { func (s *TcpdumpImpl) DumpDownload(neType, neId, taskCode string) (string, error) {
// 查询网元获取IP // 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" { if neInfo.NeId != neId || neInfo.IP == "" {
return "", "", fmt.Errorf("noData") return "", fmt.Errorf("app.common.noNEInfo")
} }
// 网元主机的SSH客户端 // 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId) sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil { if err != nil {
return "", "", err return "", err
} }
defer sshClient.Close() defer sshClient.Close()
// 网元主机的SSH客户端进行文件传输
sftpClient, err := sshClient.NewClientSFTP()
if err != nil {
return "", fmt.Errorf("ne info sftp client err")
}
defer sftpClient.Close()
// 是否拥有sudo权限并拼接 neTypeLower := strings.ToLower(neInfo.NeType)
withSudo := "" // 网管本地路径
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil { localDirPath := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s", neTypeLower, neInfo.NeId)
withSudo = "sudo " if runtime.GOOS == "windows" {
localDirPath = fmt.Sprintf("C:%s", localDirPath)
} }
if msg, err := sshClient.RunCMD(fmt.Sprintf("%s expect -version", withSudo)); err != nil { // 网元pcap目录 /tmp/omc/tcpdump/udm/001/20240817104241
// stderr: bash: expect未找到命令 => exit status 127 sshClient.RunCMD("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc")
msg = strings.TrimSpace(msg) neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", neTypeLower, neInfo.NeId, taskCode)
logger.Warnf("DumpUPF err: %s => %s", msg, err.Error()) // 网元端复制到本地
return "", "", fmt.Errorf(msg) localDirFilePath := filepath.Join(localDirPath, taskCode)
if err = sftpClient.CopyDirRemoteToLocal(neDirTemp, localDirFilePath); err != nil {
return "", fmt.Errorf("copy tcpdump file err")
} }
// 拼装命令 // 压缩zip文件名
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) zipFileName := fmt.Sprintf("%s-%s-pcap-%s.zip", neTypeLower, neInfo.NeId, taskCode)
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS) zipFilePath := filepath.Join(localDirPath, zipFileName)
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID) if err := file.CompressZipByDir(zipFilePath, localDirFilePath); err != nil {
// UPF标准版本telnet脚本 return "", fmt.Errorf("compress zip err")
scriptStr := "set pcapCmd [lindex $argv 0]\nspawn telnet " + neInfo.IP + " 5002\nexpect \"upfd1# \"\nsend \"$pcapCmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\"" }
// scriptStr := "set pcapCmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$pcapCmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
writePcapFile := fmt.Sprintf("echo '%s' > pcapUPF.sh\n %s chmod +x pcapUPF.sh", scriptStr, withSudo)
writeLogFile := fmt.Sprintf("> %s.log 2>&1 \ncat %s.log", fileName, fileName)
_ = os.RemoveAll(localDirFilePath) // 删除本地临时目录
return zipFilePath, nil
}
// UPFTrace UPF标准版内部抓包
func (s *TcpdumpImpl) UPFTrace(neType, neId, cmdStr string) (string, error) {
// 命令检查
if strings.Contains(cmdStr, "file") {
return "", fmt.Errorf("command cannot contain file")
}
// 查询网元获取IP
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return "", fmt.Errorf("app.common.noNEInfo")
}
// 网元主机的SSH客户端
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
if err != nil {
return "", err
}
defer sshClient.Close()
// 网元主机的Telnet客户端
telnetClient, err := s.neInfoService.NeRunTelnetClient("UPF", neInfo.NeId, 2)
if err != nil {
return "", err
}
defer telnetClient.Close()
// 命令拼装
fileName := fmt.Sprintf("%s_%s_part_%s.pcap ", neInfo.NeType, neInfo.NeId, time.Now().Format("20060102150405"))
pcapCmd := fmt.Sprintf("%s\r\n", cmdStr)
// 以off结尾是停止抓包不需要写文件 // 以off结尾是停止抓包不需要写文件
pcapCmd := cmdStr if !strings.Contains(cmdStr, "off") {
if !strings.HasSuffix(pcapCmd, "off") { // pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
pcapCmd = fmt.Sprintf("%s file %s.pcap", cmdStr, fileName) pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
} }
sendCmd := fmt.Sprintf("cd /tmp \n%s\n expect ./pcapUPF.sh '%s' %s", writePcapFile, pcapCmd, writeLogFile) // 发送命令 UPF内部默认输出路径/tmp只能写文件名
// cd /tmp // pcap trace rx tx max 100000 intfc any file upf_test.pcap
// echo '' > // pcap trace rx tx off
// expect ./cap.sh > pcapUPF.sh output, err := telnetClient.RunCMD(pcapCmd)
// sudo chmod +x pcapUPF.sh if err != nil {
// expect ./cap.sh 'pcap dispatch trace off' > 20240115165701_UDM_001.log 2>&1 logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
// cat 20240115165701_UDM_001.log return "", err
msg, err := sshClient.RunCMD(sendCmd)
msg = strings.TrimSpace(msg)
if err != nil || strings.HasPrefix(msg, "stderr:") {
logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
return "", "", err
} }
if strings.Contains(msg, "Unable to connect to remote host") {
return "", "", fmt.Errorf("connection refused") // 结果截取
arr := strings.Split(output, "\r\n")
if len(arr) == 2 {
return "", fmt.Errorf("trace pacp run failed")
} }
// 以off结尾是停止抓包不需要写文件 if len(arr) > 3 {
if strings.HasSuffix(pcapCmd, "off") { resMsg := arr[2]
if strings.Contains(msg, "Write ") { // pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
lastTmpIndex := strings.LastIndex(msg, "/tmp/") // pcap trace: dispatch trace already enabled...
text := msg[lastTmpIndex+5:] // pcap trace: dispatch trace already disabled...
extensionIndex := strings.LastIndex(text, ".pcap") // pcap trace: No packets captured...
if extensionIndex != -1 { // Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
fileName = text[:extensionIndex] if strings.Contains(resMsg, "unknown input") {
return "", fmt.Errorf("trace pacp unknown input")
}
if strings.Contains(resMsg, "already enabled") {
return "", fmt.Errorf("trace pacp already running")
}
if strings.Contains(resMsg, "already disabled") {
return "", fmt.Errorf("trace pacp not running")
}
if strings.Contains(resMsg, "No packets") {
return "", fmt.Errorf("trace pacp not packets")
}
if strings.Contains(resMsg, "packets to") {
matches := regexp.MustCompile(`(/tmp/[^,\s]+)`).FindStringSubmatch(resMsg)
if len(matches) == 0 {
return "", fmt.Errorf("file path not found")
} }
} else { return matches[0], nil
fileName = ""
} }
} }
return fileName, msg, err return "trace pacp running", nil
} }

View File

@@ -28,10 +28,14 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
controller.NewTcpdump.DumpStop, controller.NewTcpdump.DumpStop,
) )
tcpdumpGroup.POST("/traceUPF", tcpdumpGroup.GET("/download",
middleware.PreAuthorize(nil),
controller.NewTcpdump.DumpDownload,
)
tcpdumpGroup.POST("/upf",
middleware.PreAuthorize(nil), middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
controller.NewTcpdump.TraceUPF, controller.NewTcpdump.UPFTrace,
) )
} }
} }