289 lines
10 KiB
Go
289 lines
10 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"path/filepath"
|
||
"regexp"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"be.ems/src/framework/cmd"
|
||
"be.ems/src/framework/logger"
|
||
neService "be.ems/src/modules/network_element/service"
|
||
)
|
||
|
||
// 实例化服务层 TCPdump 结构体
|
||
var NewTCPdump = &TCPdump{
|
||
neInfoService: neService.NewNeInfo,
|
||
}
|
||
|
||
// 信令抓包 服务层处理
|
||
type TCPdump struct {
|
||
neInfoService *neService.NeInfo // 网元信息服务
|
||
}
|
||
|
||
// 抓包进程PID
|
||
var dumpPIDMap sync.Map
|
||
|
||
// DumpStart 触发tcpdump开始抓包
|
||
func (s *TCPdump) DumpStart(neType, neId, cmdStr string) (string, error) {
|
||
// 命令检查
|
||
if strings.Contains(cmdStr, "-w") {
|
||
return "", fmt.Errorf("command cannot contain -w")
|
||
}
|
||
|
||
// // 查询网元获取IP
|
||
// neInfo := s.neInfoService.FindByNeTypeAndNeID(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()
|
||
|
||
// 检查是否安装tcpdump
|
||
if msg, err := cmd.Exec("sudo tcpdump --version"); err != nil {
|
||
// bash: tcpdump: command not found
|
||
msg = strings.TrimSpace(msg)
|
||
logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
|
||
return "", fmt.Errorf("%s", msg)
|
||
}
|
||
|
||
taskCode := time.Now().Format("20060102150405")
|
||
// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
|
||
neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
|
||
cmd.Exec(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 755 -R /usr/local/omc/tcpdump", neDirTemp))
|
||
|
||
// 命令拼装
|
||
logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
|
||
filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
|
||
if strings.Contains(cmdStr, "-G") {
|
||
filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
|
||
}
|
||
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 /usr/local/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap > /usr/local/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
|
||
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /usr/local/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap > /usr/local/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 := cmd.Exec(sendCmd)
|
||
outputPID = strings.TrimSpace(outputPID)
|
||
if err != nil || strings.HasPrefix(outputPID, "stderr:") {
|
||
logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
|
||
return "", err
|
||
}
|
||
|
||
// 日志文件行号
|
||
PIDMap := s.logFileLastLine(neType)
|
||
PIDMap["neType"] = neType
|
||
PIDMap["neId"] = neId
|
||
PIDMap["taskCode"] = taskCode
|
||
PIDMap["pid"] = outputPID
|
||
PIDMap["cmd"] = sendCmd
|
||
|
||
// 检查进程 ps aux | grep tcpdump
|
||
// 强杀 sudo pkill tcpdump
|
||
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neType), neId, taskCode)
|
||
dumpPIDMap.Store(pidKey, PIDMap)
|
||
return taskCode, err
|
||
}
|
||
|
||
// DumpStop 停止已存在抓包句柄
|
||
func (s *TCPdump) DumpStop(neType, neId, taskCode string) ([]string, error) {
|
||
// // 查询网元获取IP
|
||
// neInfo := s.neInfoService.FindByNeTypeAndNeID(neType, neId)
|
||
// if neInfo.NeId != neId || neInfo.IP == "" {
|
||
// return []string{}, fmt.Errorf("app.common.noNEInfo")
|
||
// }
|
||
// // 网元主机的SSH客户端
|
||
// sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
||
// if err != nil {
|
||
// return []string{}, err
|
||
// }
|
||
// defer sshClient.Close()
|
||
|
||
// 是否存在执行过的进程
|
||
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neType), neId, taskCode)
|
||
PIDMap, ok := dumpPIDMap.Load(pidKey)
|
||
if !ok || PIDMap == nil {
|
||
return []string{}, fmt.Errorf("tcpdump is not running")
|
||
}
|
||
pid, ok := PIDMap.(map[string]string)["pid"]
|
||
if !ok || pid == "" {
|
||
return []string{}, fmt.Errorf("tcpdump is not running")
|
||
}
|
||
s.logFileLastLineToFile(PIDMap.(map[string]string))
|
||
|
||
// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
|
||
neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
|
||
// 命令拼装
|
||
sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s ls %s", pid, neDirTemp)
|
||
// pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s ls /usr/local/omc/tcpdump/udm/001/20240817104241
|
||
output, err := cmd.Exec(sendCmd)
|
||
output = strings.TrimSpace(output)
|
||
if err != nil || strings.HasPrefix(output, "ls: ") {
|
||
logger.Errorf("DumpStop err: %s => %s", output, err.Error())
|
||
return []string{}, err
|
||
}
|
||
files := strings.Split(output, "\n")
|
||
dumpPIDMap.Delete(pidKey)
|
||
return files, nil
|
||
}
|
||
|
||
// logFileLastLine 日志文件最后行号
|
||
func (s *TCPdump) logFileLastLine(neType string) map[string]string {
|
||
logFileArr := make([]string, 0)
|
||
mapFile := make(map[string]string, 0)
|
||
|
||
// 存放文件目录 /var/log/xxx.log
|
||
if neType == "IMS" {
|
||
logFileArr = append(logFileArr,
|
||
"/var/log/ims/pcscf/pcscf.log",
|
||
"/var/log/ims/bgcf/bgcf.log",
|
||
"/var/log/ims/bsf/bsf.log",
|
||
"/var/log/ims/icscf/icscf.log",
|
||
"/var/log/ims/ismc/ismc.log",
|
||
"/var/log/ims/mmtel/mmtel.log",
|
||
"/var/log/ims/scscf/scscf.log",
|
||
"/var/log/ims/iwf/iwf.log",
|
||
)
|
||
} else {
|
||
neLogFile := fmt.Sprintf("/var/log/%s.log", strings.ToLower(neType))
|
||
logFileArr = append(logFileArr, neLogFile)
|
||
}
|
||
|
||
for _, v := range logFileArr {
|
||
// lastLine, err := sshClient.RunCMD(fmt.Sprintf("sudo sed -n '$=' %s", v))
|
||
lastLine, err := cmd.Exec(fmt.Sprintf("sudo sed -n '$=' %s", v))
|
||
lastLine = strings.TrimSpace(lastLine)
|
||
if err != nil || strings.HasPrefix(lastLine, "sed: can't") {
|
||
logger.Errorf("logFileLastLine err: %s => %s", lastLine, err.Error())
|
||
continue
|
||
}
|
||
mapFile[v] = lastLine
|
||
}
|
||
return mapFile
|
||
}
|
||
|
||
// logFileLastLine 日志文件最后行号
|
||
func (s *TCPdump) logFileLastLineToFile(PIDMap map[string]string) error {
|
||
// // 网元主机的SSH客户端进行文件传输
|
||
// sftpClient, err := sshClient.NewClientSFTP()
|
||
// if err != nil {
|
||
// return fmt.Errorf("ne info sftp client err")
|
||
// }
|
||
// defer sftpClient.Close()
|
||
|
||
neType := PIDMap["neType"]
|
||
neId := PIDMap["neId"]
|
||
taskCode := PIDMap["taskCode"]
|
||
// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
|
||
neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
|
||
// sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 755 -R /usr/local/omc/tcpdump", neDirTemp))
|
||
cmd.Exec(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 755 -R /usr/local/omc/tcpdump", neDirTemp))
|
||
|
||
lastLineMap := s.logFileLastLine(neType)
|
||
for lastLogFile, lastFileLine := range lastLineMap {
|
||
for startLogFile, startFileLine := range PIDMap {
|
||
if lastLogFile == startLogFile && lastFileLine != "" {
|
||
if startFileLine == "" {
|
||
startFileLine = "1" // 起始行号从第一行开始
|
||
}
|
||
outputFile := fmt.Sprintf("%s/%s", neDirTemp, filepath.Base(lastLogFile))
|
||
// sendCmd := fmt.Sprintf("sudo sed -n \"%s,%sp\" \"%s\" | sudo tee \"%s\" > /dev/null", startFileLine, lastFileLine, lastLogFile, outputFile)
|
||
// sudo sed -n "1,5p" "/var/log/amf.log" | sudo tee "/usr/local/omc/tcpdump/amf/001/20241008141336/amf.log" > /dev/null
|
||
// output, err := sshClient.RunCMD(sendCmd)
|
||
sendCmd := fmt.Sprintf("sudo sed -n \"%s,%sp\" \"%s\" | sudo tee \"%s\" > /dev/null", startFileLine, lastFileLine, lastLogFile, outputFile)
|
||
output, err := cmd.Exec(sendCmd)
|
||
if err != nil || strings.HasPrefix(output, "stderr:") {
|
||
logger.Errorf("logFileLastLineToFile err: %s => %s", strings.TrimSpace(output), err.Error())
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// UPFTrace UPF标准版内部抓包
|
||
func (s *TCPdump) UPFTrace(neType, neId, cmdStr string) (string, error) {
|
||
// 命令检查
|
||
if strings.Contains(cmdStr, "file") {
|
||
return "", fmt.Errorf("command cannot contain file")
|
||
}
|
||
|
||
// 查询网元获取IP
|
||
neInfo := s.neInfoService.FindByNeTypeAndNeID(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结尾是停止抓包,不需要写文件
|
||
if !strings.Contains(cmdStr, "off") {
|
||
// pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
|
||
pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
|
||
}
|
||
// 发送命令 UPF内部默认输出路径/tmp只能写文件名
|
||
// pcap trace rx tx max 100000 intfc any file upf_test.pcap
|
||
// pcap trace rx tx off
|
||
output, err := telnetClient.RunCMD(pcapCmd)
|
||
if err != nil {
|
||
logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
|
||
return "", err
|
||
}
|
||
|
||
// 结果截取
|
||
arr := strings.Split(output, "\r\n")
|
||
if len(arr) == 2 {
|
||
return "", fmt.Errorf("trace pacp run failed")
|
||
}
|
||
if len(arr) > 3 {
|
||
resMsg := arr[2]
|
||
// pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
|
||
// pcap trace: dispatch trace already enabled...
|
||
// pcap trace: dispatch trace already disabled...
|
||
// pcap trace: No packets captured...
|
||
// Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
|
||
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")
|
||
}
|
||
return matches[0], nil
|
||
}
|
||
}
|
||
return "trace pacp running", nil
|
||
}
|