//go:build !windows // +build !windows package staros import ( "errors" "io/ioutil" "os" "strconv" "strings" "time" ) func NetUsage() ([]NetAdapter, error) { data, err := ioutil.ReadFile("/proc/net/dev") if err != nil { return []NetAdapter{}, err } sps := strings.Split(strings.TrimSpace(string(data)), "\n") if len(sps) < 3 { return []NetAdapter{}, errors.New("No Adaptor") } var res []NetAdapter netLists := sps[2:] for _, v := range netLists { v = strings.ReplaceAll(v, " ", " ") for strings.Contains(v, " ") { v = strings.ReplaceAll(v, " ", " ") } v = strings.TrimSpace(v) card := strings.Split(v, " ") name := strings.ReplaceAll(card[0], ":", "") recvBytes, _ := strconv.Atoi(card[1]) sendBytes, _ := strconv.Atoi(card[9]) res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)}) } return res, nil } func NetUsageByname(name string) (NetAdapter, error) { ada, err := NetUsage() if err != nil { return NetAdapter{}, err } for _, v := range ada { if v.Name == name { return v, nil } } return NetAdapter{}, errors.New("Not Found") } func NetSpeeds(duration time.Duration) ([]NetSpeed, error) { list1, err := NetUsage() if err != nil { return []NetSpeed{}, err } time.Sleep(duration) list2, err := NetUsage() if err != nil { return []NetSpeed{}, err } if len(list1) > len(list2) { return []NetSpeed{}, errors.New("NetWork Adaptor Num Not ok") } var res []NetSpeed for k, v := range list1 { recv := float64(list2[k].RecvBytes-v.RecvBytes) / duration.Seconds() send := float64(list2[k].SendBytes-v.SendBytes) / duration.Seconds() res = append(res, NetSpeed{ Name: v.Name, RecvSpeeds: recv, SendSpeeds: send, RecvBytes: list2[k].RecvBytes, SendBytes: list2[k].SendBytes, }) } return res, nil } func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) { ada, err := NetSpeeds(duration) if err != nil { return NetSpeed{}, err } for _, v := range ada { if v.Name == name { return v, nil } } return NetSpeed{}, errors.New("Not Found") } // NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections // if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps func NetConnections(analysePid bool, types string) ([]NetConn, error) { var result []NetConn var inodeMap map[string]int64 var err error var fileList []string if types == "" || strings.Contains(strings.ToLower(types), "all") { fileList = []string{ "/proc/net/tcp", "/proc/net/tcp6", "/proc/net/udp", "/proc/net/udp6", "/proc/net/unix", } } if strings.Contains(strings.ToLower(types), "tcp") { fileList = append(fileList, "/proc/net/tcp", "/proc/net/tcp6") } if strings.Contains(strings.ToLower(types), "udp") { fileList = append(fileList, "/proc/net/udp", "/proc/net/udp6") } if strings.Contains(strings.ToLower(types), "unix") { fileList = append(fileList, "/proc/net/unix") } if analysePid { inodeMap, err = GetInodeMap() if err != nil { return result, err } } for _, file := range fileList { data, err := ioutil.ReadFile(file) if err != nil { return result, err } tmpRes, err := analyseNetFiles(data, inodeMap, file[strings.LastIndex(file, "/")+1:]) if err != nil { return result, err } result = append(result, tmpRes...) } return result, nil } func GetInodeMap() (map[string]int64, error) { res := make(map[string]int64) paths, err := ioutil.ReadDir("/proc") if err != nil { return nil, err } for _, v := range paths { if v.IsDir() && Exists("/proc/"+v.Name()+"/fd") { fds, err := ioutil.ReadDir("/proc/" + v.Name() + "/fd") if err != nil && Exists("/proc/"+v.Name()+"/fd") { return nil, err } for _, fd := range fds { socket, err := os.Readlink("/proc/" + v.Name() + "/fd/" + fd.Name()) if err != nil { continue } if !strings.Contains(socket, "socket") { continue } start := strings.Index(socket, "[") if start < 0 { continue } pid, err := strconv.ParseInt(v.Name(), 10, 64) if err != nil { break } res[socket[start+1:len(socket)-1]] = pid } } } return res, err } func analyseNetFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) { if typed == "unix" { return analyseUnixFiles(data, inodeMap, typed) } var result []NetConn strdata := strings.TrimSpace(string(data)) strdata = remainOne(strdata, " ", " ") csvData := strings.Split(strdata, "\n") pidMap := make(map[int64]*Process) for line, lineData := range csvData { if line == 0 { continue } v := strings.Split(strings.TrimSpace(lineData), " ") var res NetConn ip, port, err := parseHexIpPort(v[1]) if err != nil { return result, err } res.LocalAddr = ip res.LocalPort = port ip, port, err = parseHexIpPort(v[2]) if err != nil { return result, err } res.RemoteAddr = ip res.RemotePort = port //connection state if strings.Contains(typed, "tcp") { state, err := strconv.ParseInt(strings.TrimSpace(v[3]), 16, 64) if err != nil { return result, err } res.Status = TCP_STATE[state] } txrx_queue := strings.Split(strings.TrimSpace(v[4]), ":") if len(txrx_queue) != 2 { return result, errors.New("not a valid net file") } tx_queue, err := strconv.ParseInt(txrx_queue[0], 16, 64) if err != nil { return result, err } res.TX_Queue = tx_queue rx_queue, err := strconv.ParseInt(txrx_queue[1], 16, 64) if err != nil { return result, err } res.RX_Queue = rx_queue timer := strings.Split(strings.TrimSpace(v[5]), ":") if len(timer) != 2 { return result, errors.New("not a valid net file") } switch timer[0] { case "00": res.TimerActive = "NO_TIMER" case "01": //重传定时器 res.TimerActive = "RETRANSMIT" case "02": //连接定时器、FIN_WAIT_2定时器或TCP保活定时器 res.TimerActive = "KEEPALIVE" case "03": //TIME_WAIT定时器 res.TimerActive = "TIME_WAIT" case "04": //持续定时器 res.TimerActive = "ZERO_WINDOW_PROBE" default: res.TimerActive = "UNKNOWN" } timerJif, err := strconv.ParseInt(timer[1], 16, 64) if err != nil { return result, err } res.TimerJiffies = timerJif timerCnt, err := strconv.ParseInt(strings.TrimSpace(v[6]), 16, 64) if err != nil { return result, err } res.RtoTimer = timerCnt res.Uid, err = strconv.ParseInt(v[7], 10, 64) if err != nil { return result, err } res.Inode = v[9] if inodeMap != nil && len(inodeMap) > 0 { var ok bool res.Pid, ok = inodeMap[res.Inode] if !ok { res.Pid = -1 } else { _, ok := pidMap[res.Pid] if !ok { tmp, err := FindProcessByPid(res.Pid) if err != nil { pidMap[res.Pid] = nil } else { pidMap[res.Pid] = &tmp } } res.Process = pidMap[res.Pid] } } res.Typed = typed result = append(result, res) } return result, nil } func analyseUnixFiles(data []byte, inodeMap map[string]int64, typed string) ([]NetConn, error) { var result []NetConn strdata := strings.TrimSpace(string(data)) strdata = remainOne(strdata, " ", " ") csvData := strings.Split(strdata, "\n") pidMap := make(map[int64]*Process) for line, lineData := range csvData { if line == 0 { continue } v := strings.Split(strings.TrimSpace(lineData), " ") var res NetConn res.Inode = v[6] if len(v) == 8 { res.Socket = v[7] } if inodeMap != nil && len(inodeMap) > 0 { var ok bool res.Pid, ok = inodeMap[res.Inode] if !ok { res.Pid = -1 } else { _, ok := pidMap[res.Pid] if !ok || pidMap[res.Pid] == nil { tmp, err := FindProcessByPid(res.Pid) if err != nil { pidMap[res.Pid] = nil } else { pidMap[res.Pid] = &tmp } } if pidMap[res.Pid] != nil { res.Uid = int64(pidMap[res.Pid].RUID) res.Process = pidMap[res.Pid] } } } res.Typed = typed result = append(result, res) } return result, nil }