staros/network_unix.go

391 lines
8.9 KiB
Go
Raw Permalink Normal View History

//go:build linux
// +build linux
2021-06-04 10:44:53 +08:00
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 nil, err
2021-06-04 10:44:53 +08:00
}
sps := strings.Split(strings.TrimSpace(string(data)), "\n")
if len(sps) < 3 {
return nil, errors.New("No Adaptor")
2021-06-04 10:44:53 +08:00
}
var res []NetAdapter
netLists := sps[2:]
for _, v := range netLists {
parts := strings.SplitN(strings.TrimSpace(v), ":", 2)
if len(parts) != 2 {
continue
}
card := strings.Fields(parts[1])
if len(card) < 16 {
continue
}
name := strings.TrimSpace(parts[0])
recvBytes, err := strconv.ParseUint(card[0], 10, 64)
if err != nil {
continue
}
sendBytes, err := strconv.ParseUint(card[8], 10, 64)
if err != nil {
continue
}
2021-06-04 10:44:53 +08:00
res = append(res, NetAdapter{name, uint64(recvBytes), uint64(sendBytes)})
}
if len(res) == 0 {
return nil, errors.New("No Adaptor")
}
2021-06-04 10:44:53 +08:00
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) {
if duration <= 0 {
return nil, errors.New("duration must be positive")
}
2021-06-04 10:44:53 +08:00
list1, err := NetUsage()
if err != nil {
return nil, err
2021-06-04 10:44:53 +08:00
}
time.Sleep(duration)
list2, err := NetUsage()
if err != nil {
return nil, err
2021-06-04 10:44:53 +08:00
}
byName := make(map[string]NetAdapter, len(list2))
for _, item := range list2 {
byName[item.Name] = item
2021-06-04 10:44:53 +08:00
}
var res []NetSpeed
for _, v := range list1 {
next, ok := byName[v.Name]
if !ok {
continue
}
var recvDelta, sendDelta uint64
if next.RecvBytes >= v.RecvBytes {
recvDelta = next.RecvBytes - v.RecvBytes
}
if next.SendBytes >= v.SendBytes {
sendDelta = next.SendBytes - v.SendBytes
}
recv := float64(recvDelta) / duration.Seconds()
send := float64(sendDelta) / duration.Seconds()
2023-02-03 13:53:42 +08:00
res = append(res, NetSpeed{
Name: v.Name,
RecvSpeeds: recv,
SendSpeeds: send,
RecvBytes: next.RecvBytes,
SendBytes: next.SendBytes,
2023-02-03 13:53:42 +08:00
})
2021-06-04 10:44:53 +08:00
}
if len(res) == 0 {
return nil, errors.New("NetWork Adaptor Num Not ok")
}
2021-06-04 10:44:53 +08:00
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
2023-02-03 13:53:42 +08:00
func NetConnections(analysePid bool, types string) ([]NetConn, error) {
2021-06-04 10:44:53 +08:00
var result []NetConn
var inodeMap map[string]int64
var err error
2021-09-01 11:01:51 +08:00
var fileList []string
types = strings.ToLower(types)
if types == "" || strings.Contains(types, "all") {
2021-09-01 11:01:51 +08:00
fileList = []string{
"/proc/net/tcp",
"/proc/net/tcp6",
"/proc/net/udp",
"/proc/net/udp6",
"/proc/net/unix",
}
} else {
if strings.Contains(types, "tcp") {
fileList = append(fileList, "/proc/net/tcp", "/proc/net/tcp6")
}
if strings.Contains(types, "udp") {
fileList = append(fileList, "/proc/net/udp", "/proc/net/udp6")
}
if strings.Contains(types, "unix") {
fileList = append(fileList, "/proc/net/unix")
}
2021-09-01 11:01:51 +08:00
}
fileList = uniqueStrings(fileList)
if len(fileList) == 0 {
return nil, errors.New("unsupported net connection type")
2021-06-04 10:44:53 +08:00
}
if analysePid {
inodeMap, err = GetInodeMap()
if err != nil {
inodeMap = nil
2021-06-04 10:44:53 +08:00
}
}
for _, file := range fileList {
data, err := ioutil.ReadFile(file)
if err != nil {
if os.IsNotExist(err) {
continue
}
2021-06-04 10:44:53 +08:00
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 uniqueStrings(items []string) []string {
if len(items) == 0 {
return nil
}
seen := make(map[string]struct{}, len(items))
res := make([]string, 0, len(items))
for _, item := range items {
if _, ok := seen[item]; ok {
continue
}
seen[item] = struct{}{}
res = append(res, item)
}
return res
}
2021-06-04 10:44:53 +08:00
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
}
2021-09-01 11:01:51 +08:00
if !strings.Contains(socket, "socket") {
continue
}
2021-06-04 10:44:53 +08:00
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
}
}
}
2021-09-01 11:01:51 +08:00
return res, err
2021-06-04 10:44:53 +08:00
}
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), " ")
if len(v) < 10 {
continue
}
2021-06-04 10:44:53 +08:00
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
2021-09-01 11:01:51 +08:00
//connection state
if strings.Contains(typed, "tcp") {
state, err := strconv.ParseInt(strings.TrimSpace(v[3]), 16, 64)
if err != nil {
return result, err
}
if state >= 0 && int(state) < len(TCP_STATE) {
res.Status = TCP_STATE[state]
} else {
res.Status = TCP_STATE[TCP_UNKNOWN]
}
2021-09-01 11:01:51 +08:00
}
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
2021-06-04 10:44:53 +08:00
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), " ")
if len(v) < 7 {
continue
}
2021-06-04 10:44:53 +08:00
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]
2021-09-01 11:01:51 +08:00
if !ok || pidMap[res.Pid] == nil {
2021-06-04 10:44:53 +08:00
tmp, err := FindProcessByPid(res.Pid)
if err != nil {
pidMap[res.Pid] = nil
} else {
pidMap[res.Pid] = &tmp
}
}
2021-09-01 11:01:51 +08:00
if pidMap[res.Pid] != nil {
res.Uid = int64(pidMap[res.Pid].RUID)
res.Process = pidMap[res.Pid]
}
2021-06-04 10:44:53 +08:00
}
}
res.Typed = typed
result = append(result, res)
}
return result, nil
}