net/linux: use NETLINK_SOCK_DIAG

To retrieve network information use NETLINK_SOCK_DIAG instead of walking
/proc/<PID> and parsing files.
Consequently this reduces the number of syscalls, as walking /proc/<PID> and
opening, reading and closing files is no longer required. But it also reduces
the memory footprint as reading files into memory for processing is no longer
required.

Related issues:
- https://github.com/shirou/gopsutil/issues/695
- https://github.com/shirou/gopsutil/issues/784

Supersedes https://github.com/shirou/gopsutil/pull/809

Signed-off-by: Florian Lehner <dev@der-flo.net>
pull/1660/head
Florian Lehner 12 months ago
parent 184fb1ccbc
commit 15f5f5aee3

@ -3,27 +3,25 @@ module github.com/shirou/gopsutil/v4
go 1.18
require (
github.com/florianl/go-diag v0.0.2
github.com/google/go-cmp v0.6.0
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0
github.com/mdlayher/socket v0.5.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c
github.com/shoenig/go-m1cpu v0.1.6
github.com/stretchr/testify v1.9.0
github.com/tklauser/go-sysconf v0.3.12
github.com/yusufpapurcu/wmi v1.2.4
golang.org/x/sys v0.20.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.1-0.20240506173926-6dfb94eaa3bd
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

@ -1,12 +1,22 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/florianl/go-diag v0.0.1 h1:NjjG/z0eQ7J6+KrIv3hv5weH6Lxq+KBr45HPc/qdRQo=
github.com/florianl/go-diag v0.0.1/go.mod h1:Rkjs8DWYe7g4wuw4BQbYXBuUU7vaf1s2ym8N+QpCZt0=
github.com/florianl/go-diag v0.0.2 h1:4zqTkVM4egFpsEbWVh9ZAam+maGEjPIuzEAcXXsSSl4=
github.com/florianl/go-diag v0.0.2/go.mod h1:Rkjs8DWYe7g4wuw4BQbYXBuUU7vaf1s2ym8N+QpCZt0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
@ -22,12 +32,16 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.1-0.20240506173926-6dfb94eaa3bd h1:NY5cSUUSJ8nWcXOjwj7e1D9//oy6+uKCq32Dnt/p9EQ=
golang.org/x/sys v0.20.1-0.20240506173926-6dfb94eaa3bd/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

@ -4,19 +4,18 @@
package net
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"syscall"
"github.com/florianl/go-diag"
"github.com/shirou/gopsutil/v4/internal/common"
"golang.org/x/sys/unix"
)
const ( // Conntrack Column numbers
@ -305,24 +304,24 @@ func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, erro
}
// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
var tcpStatuses = map[string]string{
"01": "ESTABLISHED",
"02": "SYN_SENT",
"03": "SYN_RECV",
"04": "FIN_WAIT1",
"05": "FIN_WAIT2",
"06": "TIME_WAIT",
"07": "CLOSE",
"08": "CLOSE_WAIT",
"09": "LAST_ACK",
"0A": "LISTEN",
"0B": "CLOSING",
var tcpStatuses = map[uint8]string{
1: "ESTABLISHED",
2: "SYN_SENT",
3: "SYN_RECV",
4: "FIN_WAIT1",
5: "FIN_WAIT2",
6: "TIME_WAIT",
7: "CLOSE",
8: "CLOSE_WAIT",
9: "LAST_ACK",
10: "LISTEN",
11: "CLOSING",
}
type netConnectionKindType struct {
filename string
family uint32
sockType uint32
filename string
}
var kindTCP4 = netConnectionKindType{
@ -374,15 +373,15 @@ type inodeMap struct {
}
type connTmp struct {
fd uint32
family uint32
sockType uint32
status string
path string
laddr Addr
raddr Addr
status string
fd uint32
sockType uint32
pid int32
boundPid int32
path string
family uint8
}
// Return a list of network connections opened.
@ -460,7 +459,7 @@ func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, p
}
root := common.HostProcWithContext(ctx)
var err error
var inodes map[string][]inodeMap
var inodes map[uint32][]inodeMap
if pid == 0 {
inodes, err = getProcInodesAllWithContext(ctx, root, max)
} else {
@ -476,33 +475,31 @@ func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, p
return statsFromInodesWithContext(ctx, root, pid, tmap, inodes, skipUids)
}
func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
return statsFromInodesWithContext(context.Background(), root, pid, tmap, inodes, skipUids)
}
func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tmap []netConnectionKindType, inodes map[uint32][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
dupCheckMap := make(map[string]struct{})
var ret []ConnectionStat
var err error
// open a netlink socket
nl, err := diag.Open(&diag.Config{})
if err != nil {
return nil, err
}
defer nl.Close()
for _, t := range tmap {
var path string
var connKey string
var ls []connTmp
if pid == 0 {
path = fmt.Sprintf("%s/net/%s", root, t.filename)
} else {
path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
}
switch t.family {
case syscall.AF_INET, syscall.AF_INET6:
ls, err = processInetWithContext(ctx, path, t, inodes, pid)
ls, err = processNetDiagWithContext(nl, t, inodes, pid)
case syscall.AF_UNIX:
ls, err = processUnix(path, t, inodes, pid)
ls, err = processUnixDiagWithContext(nl, inodes, pid)
}
if err != nil {
return nil, err
}
for _, c := range ls {
// Build TCP key to id the connection uniquely
// socket type, src ip, src port, dst ip, dst port and state should be enough
@ -514,8 +511,8 @@ func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tma
conn := ConnectionStat{
Fd: c.fd,
Family: c.family,
Type: c.sockType,
Family: uint32(c.family),
Type: uint32(c.sockType),
Laddr: c.laddr,
Raddr: c.raddr,
Status: c.status,
@ -543,8 +540,8 @@ func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tma
}
// getProcInodes returns fd of the pid.
func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap)
func getProcInodes(root string, pid int32, max int) (map[uint32][]inodeMap, error) {
ret := make(map[uint32][]inodeMap)
dir := fmt.Sprintf("%s/%d/fd", root, pid)
f, err := os.Open(dir)
@ -559,16 +556,21 @@ func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, erro
for _, dirEntry := range dirEntries {
inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, dirEntry.Name())
inode, err := os.Readlink(inodePath)
inodeStr, err := os.Readlink(inodePath)
if err != nil {
continue
}
if !strings.HasPrefix(inode, "socket:[") {
if !strings.HasPrefix(inodeStr, "socket:[") {
continue
}
// the process is using a socket
l := len(inode)
inode = inode[8 : l-1]
l := len(inodeStr)
inodeStr = inodeStr[8 : l-1]
tmp, err := strconv.Atoi(inodeStr)
if err != nil {
return ret, err
}
inode := uint32(tmp)
_, ok := ret[inode]
if !ok {
ret[inode] = make([]inodeMap, 0)
@ -625,8 +627,8 @@ func PidsWithContext(ctx context.Context) ([]int32, error) {
// FIXME: Import process occures import cycle.
// see remarks on pids()
type process struct {
Pid int32 `json:"pid"`
uids []int32
Pid int32 `json:"pid"`
}
// Uids returns user ids of the process as a slice of the int
@ -668,16 +670,16 @@ func (p *process) fillFromStatus(ctx context.Context) error {
return nil
}
func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
func getProcInodesAll(root string, max int) (map[uint32][]inodeMap, error) {
return getProcInodesAllWithContext(context.Background(), root, max)
}
func getProcInodesAllWithContext(ctx context.Context, root string, max int) (map[string][]inodeMap, error) {
func getProcInodesAllWithContext(ctx context.Context, root string, max int) (map[uint32][]inodeMap, error) {
pids, err := PidsWithContext(ctx)
if err != nil {
return nil, err
}
ret := make(map[string][]inodeMap)
ret := make(map[uint32][]inodeMap)
for _, pid := range pids {
t, err := getProcInodes(root, pid, max)
@ -697,48 +699,6 @@ func getProcInodesAllWithContext(ctx context.Context, root string, max int) (map
return ret, nil
}
// decodeAddress decode addresse represents addr in proc/net/*
// ex:
// "0500000A:0016" -> "10.0.0.5", 22
// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
func decodeAddress(family uint32, src string) (Addr, error) {
return decodeAddressWithContext(context.Background(), family, src)
}
func decodeAddressWithContext(ctx context.Context, family uint32, src string) (Addr, error) {
t := strings.Split(src, ":")
if len(t) != 2 {
return Addr{}, fmt.Errorf("does not contain port, %s", src)
}
addr := t[0]
port, err := strconv.ParseUint(t[1], 16, 16)
if err != nil {
return Addr{}, fmt.Errorf("invalid port, %s", src)
}
decoded, err := hex.DecodeString(addr)
if err != nil {
return Addr{}, fmt.Errorf("decode error, %w", err)
}
var ip net.IP
if family == syscall.AF_INET {
if common.IsLittleEndian() {
ip = net.IP(ReverseWithContext(ctx, decoded))
} else {
ip = net.IP(decoded)
}
} else { // IPv6
ip, err = parseIPv6HexStringWithContext(ctx, decoded)
if err != nil {
return Addr{}, err
}
}
return Addr{
IP: ip.String(),
Port: uint32(port),
}, nil
}
// Reverse reverses array of bytes.
func Reverse(s []byte) []byte {
return ReverseWithContext(context.Background(), s)
@ -751,59 +711,32 @@ func ReverseWithContext(ctx context.Context, s []byte) []byte {
return s
}
// parseIPv6HexString parse array of bytes to IPv6 string
func parseIPv6HexString(src []byte) (net.IP, error) {
return parseIPv6HexStringWithContext(context.Background(), src)
}
func processNetDiagWithContext(nl *diag.Diag, kind netConnectionKindType, inodes map[uint32][]inodeMap, filterPid int32) ([]connTmp, error) {
var ret []connTmp
func parseIPv6HexStringWithContext(ctx context.Context, src []byte) (net.IP, error) {
if len(src) != 16 {
return nil, fmt.Errorf("invalid IPv6 string")
opt := &diag.NetOption{
Family: uint8(kind.family),
State: ^uint32(0),
}
buf := make([]byte, 0, 16)
for i := 0; i < len(src); i += 4 {
r := ReverseWithContext(ctx, src[i:i+4])
buf = append(buf, r...)
switch kind.sockType {
case 1:
opt.Protocol = unix.IPPROTO_TCP
case 2:
opt.Protocol = unix.IPPROTO_UDP
default:
return nil, fmt.Errorf("unhandled socket type")
}
return net.IP(buf), nil
}
func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
return processInetWithContext(context.Background(), file, kind, inodes, filterPid)
}
func processInetWithContext(ctx context.Context, file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
if strings.HasSuffix(file, "6") && !common.PathExists(file) {
// IPv6 not supported, return empty.
return []connTmp{}, nil
}
// Read the contents of the /proc file with a single read sys call.
// This minimizes duplicates in the returned connections
// For more info:
// https://github.com/shirou/gopsutil/pull/361
contents, err := os.ReadFile(file)
conns, err := nl.NetDump(opt)
if err != nil {
return nil, err
}
lines := bytes.Split(contents, []byte("\n"))
var ret []connTmp
// skip first line
for _, line := range lines[1:] {
l := strings.Fields(string(line))
if len(l) < 10 {
continue
}
laddr := l[1]
raddr := l[2]
status := l[3]
inode := l[9]
for _, conn := range conns {
pid := int32(0)
fd := uint32(0)
i, exists := inodes[inode]
i, exists := inodes[conn.INode]
if exists {
pid = i[0].pid
fd = i[0].fd
@ -811,62 +744,56 @@ func processInetWithContext(ctx context.Context, file string, kind netConnection
if filterPid > 0 && filterPid != pid {
continue
}
if kind.sockType == syscall.SOCK_STREAM {
status = tcpStatuses[status]
} else {
status = "NONE"
}
la, err := decodeAddressWithContext(ctx, kind.family, laddr)
src, err := diag.ToNetipAddrWithFamily(conn.DiagMsg.Family, conn.ID.Src)
if err != nil {
continue
}
ra, err := decodeAddressWithContext(ctx, kind.family, raddr)
srcPort := diag.Ntohs(conn.ID.SPort)
dst, err := diag.ToNetipAddrWithFamily(conn.DiagMsg.Family, conn.ID.Dst)
if err != nil {
continue
}
dstPort := diag.Ntohs(conn.ID.DPort)
status := "NONE"
if kind.sockType == syscall.SOCK_STREAM {
status = tcpStatuses[conn.State]
}
ret = append(ret, connTmp{
fd: fd,
family: kind.family,
family: conn.Family,
sockType: kind.sockType,
laddr: la,
raddr: ra,
status: status,
pid: pid,
laddr: Addr{
IP: src.String(),
Port: uint32(srcPort),
},
raddr: Addr{
IP: dst.String(),
Port: uint32(dstPort),
},
pid: pid,
status: status,
})
}
return ret, nil
}
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
// Read the contents of the /proc file with a single read sys call.
// This minimizes duplicates in the returned connections
// For more info:
// https://github.com/shirou/gopsutil/pull/361
contents, err := os.ReadFile(file)
func processUnixDiagWithContext(nl *diag.Diag, inodes map[uint32][]inodeMap, filterPid int32) ([]connTmp, error) {
var ret []connTmp
conns, err := nl.UnixDump(&diag.UnixOption{
State: ^uint32(0),
Show: ^uint32(0),
})
if err != nil {
return nil, err
}
lines := bytes.Split(contents, []byte("\n"))
var ret []connTmp
// skip first line
for _, line := range lines[1:] {
tokens := strings.Fields(string(line))
if len(tokens) < 6 {
continue
}
st, err := strconv.Atoi(tokens[4])
if err != nil {
return nil, err
}
inode := tokens[6]
for _, conn := range conns {
var pairs []inodeMap
pairs, exists := inodes[inode]
pairs, exists := inodes[conn.Ino]
if !exists {
pairs = []inodeMap{
{},
@ -877,13 +804,13 @@ func processUnix(file string, kind netConnectionKindType, inodes map[string][]in
continue
}
var path string
if len(tokens) == 8 {
path = tokens[len(tokens)-1]
if conn.Name != nil {
path = *conn.Name
}
ret = append(ret, connTmp{
fd: pair.fd,
family: kind.family,
sockType: uint32(st),
family: conn.Family,
sockType: uint32(conn.Type),
laddr: Addr{
IP: path,
},
@ -897,7 +824,7 @@ func processUnix(file string, kind netConnectionKindType, inodes map[string][]in
return ret, nil
}
func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap {
func updateMap(src map[uint32][]inodeMap, add map[uint32][]inodeMap) map[uint32][]inodeMap {
for key, value := range add {
a, exists := src[key]
if !exists {

@ -7,7 +7,6 @@ import (
"net"
"os"
"strings"
"syscall"
"testing"
"github.com/stretchr/testify/assert"
@ -129,66 +128,6 @@ func TestConnectionsMax(t *testing.T) {
}
}
type AddrTest struct {
IP string
Port int
Error bool
}
func TestDecodeAddress(t *testing.T) {
assert := assert.New(t)
addr := map[string]AddrTest{
"11111:0035": {
Error: true,
},
"0100007F:BLAH": {
Error: true,
},
"0085002452100113070057A13F025401:0035": {
IP: "2400:8500:1301:1052:a157:7:154:23f",
Port: 53,
},
"00855210011307F025401:0035": {
Error: true,
},
}
if common.IsLittleEndian() {
addr["0500000A:0016"] = AddrTest{
IP: "10.0.0.5",
Port: 22,
}
addr["0100007F:D1C2"] = AddrTest{
IP: "127.0.0.1",
Port: 53698,
}
} else {
addr["0A000005:0016"] = AddrTest{
IP: "10.0.0.5",
Port: 22,
}
addr["7F000001:D1C2"] = AddrTest{
IP: "127.0.0.1",
Port: 53698,
}
}
for src, dst := range addr {
family := syscall.AF_INET
if len(src) > 13 {
family = syscall.AF_INET6
}
addr, err := decodeAddress(uint32(family), src)
if dst.Error {
assert.NotNil(err, src)
} else {
assert.Nil(err, src)
assert.Equal(dst.IP, addr.IP, src)
assert.Equal(dst.Port, int(addr.Port), src)
}
}
}
func TestReverse(t *testing.T) {
src := []byte{0x01, 0x02, 0x03}
assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src))

Loading…
Cancel
Save