package service import ( "fmt" "strings" "time" "ems.agt/src/framework/config" "ems.agt/src/framework/logger" "ems.agt/src/framework/utils/cmd" "ems.agt/src/framework/utils/date" neService "ems.agt/src/modules/network_element/service" ) // 实例化服务层 TcpdumpImpl 结构体 var NewTcpdumpImpl = &TcpdumpImpl{ neInfoService: neService.NewNeInfoImpl, tcpdumpPIDMap: map[string]string{}, } // 信令抓包 服务层处理 type TcpdumpImpl struct { // 网元信息服务 neInfoService neService.INeInfo // 抓包进程PID tcpdumpPIDMap map[string]string } // DumpStart 触发tcpdump开始抓包 filePcapName, err func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) { // 检查网元信息 neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) if neInfo.NeId != neId { return "", fmt.Errorf("noData") } // SSH命令 usernameNe := config.Get("ne.user").(string) // 网元统一用户 sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP) // 是否拥有sudo权限并拼接 withSudo := "" if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil { withSudo = "sudo " } if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s tcpdump --version", withSudo)); err != nil { // stderr: bash: tcpdump:未找到命令 => exit status 127 msg = strings.TrimSpace(msg) logger.Warnf("DumpStart err: %s => %s", msg, err.Error()) return "", fmt.Errorf(msg) } // 拼装命令 neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS) fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID) 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 $! msg, err := cmd.ExecWithCheck("ssh", sshHost, sendCmd) msg = strings.TrimSpace(msg) if err != nil || strings.HasPrefix(msg, "stderr:") { logger.Warnf("DumpStart err: %s => %s", msg, err.Error()) return "", err } // 检查进程 ps aux | grep tcpdump // 强杀 sudo pkill tcpdump s.tcpdumpPIDMap[neTypeID] = msg return fileName, err } // DumpStop 停止已存在抓包句柄 func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) { // 检查网元信息 neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) if neInfo.NeId != neId { return "", fmt.Errorf("noData") } // SSH命令 usernameNe := config.Get("ne.user").(string) // 网元统一用户 sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP) // 是否拥有sudo权限并拼接 withSudo := "" if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil { withSudo = "sudo " } // 是否存在进程 neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) pid, ok := s.tcpdumpPIDMap[neTypeID] if !ok || pid == "" { return "", fmt.Errorf("tcpdump is not running") } // 查看日志 viewLogFile := "" if fileName != "" && strings.Contains(fileName, neTypeID) { viewLogFile = fmt.Sprintf("\n cat %s.log", fileName) } // 拼装命令 sendCmd := fmt.Sprintf("cd /tmp \n %s kill %s %s", withSudo, pid, viewLogFile) msg, err := cmd.ExecWithCheck("ssh", sshHost, 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 msg, nil } // DumpUPF UPF标准版抓包 func (s *TcpdumpImpl) DumpUPF(neType, neId, cmdStr string) (string, string, error) { // 检查网元信息 neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId) if neInfo.NeId != neId { return "", "", fmt.Errorf("noData") } // SSH命令 usernameNe := config.Get("ne.user").(string) // 网元统一用户 sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP) // 是否拥有sudo权限并拼接 withSudo := "" if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil { withSudo = "sudo " } if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s expect -version", withSudo)); err != nil { // stderr: bash: expect:未找到命令 => exit status 127 msg = strings.TrimSpace(msg) logger.Warnf("DumpUPF err: %s => %s", msg, err.Error()) return "", "", fmt.Errorf(msg) } // 拼装命令 neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS) fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID) // UPF标准版本telnet脚本 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) // 以off结尾是停止抓包,不需要写文件 pcapCmd := cmdStr if !strings.HasSuffix(pcapCmd, "off") { pcapCmd = fmt.Sprintf("%s file %s.pcap", cmdStr, fileName) } sendCmd := fmt.Sprintf("cd /tmp \n%s\n expect ./pcapUPF.sh '%s' %s", writePcapFile, pcapCmd, writeLogFile) // cd /tmp // echo '' > // expect ./cap.sh > pcapUPF.sh // sudo chmod +x pcapUPF.sh // expect ./cap.sh 'pcap dispatch trace off' > 20240115165701_UDM_001.log 2>&1 // cat 20240115165701_UDM_001.log msg, err := cmd.ExecWithCheck("ssh", sshHost, 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") } // 以off结尾是停止抓包,不需要写文件 if strings.HasSuffix(pcapCmd, "off") { if strings.Contains(msg, "Write ") { lastTmpIndex := strings.LastIndex(msg, "/tmp/") text := msg[lastTmpIndex+5:] extensionIndex := strings.LastIndex(text, ".pcap") if extensionIndex != -1 { fileName = text[:extensionIndex] } } else { fileName = "" } } return fileName, msg, err }