github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/net/net_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 package net 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/hex" 10 "errors" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "net" 15 "os" 16 "strconv" 17 "strings" 18 "syscall" 19 20 "github.com/unionj-cloud/go-doudou/toolkit/internal/common" 21 ) 22 23 const ( // Conntrack Column numbers 24 ctENTRIES = iota 25 ctSEARCHED 26 ctFOUND 27 ctNEW 28 ctINVALID 29 ctIGNORE 30 ctDELETE 31 ctDELETE_LIST 32 ctINSERT 33 ctINSERT_FAILED 34 ctDROP 35 ctEARLY_DROP 36 ctICMP_ERROR 37 CT_EXPEctNEW 38 ctEXPECT_CREATE 39 CT_EXPEctDELETE 40 ctSEARCH_RESTART 41 ) 42 43 // NetIOCounters returns network I/O statistics for every network 44 // interface installed on the system. If pernic argument is false, 45 // return only sum of all information (which name is 'all'). If true, 46 // every network interface installed on the system is returned 47 // separately. 48 func IOCounters(pernic bool) ([]IOCountersStat, error) { 49 return IOCountersWithContext(context.Background(), pernic) 50 } 51 52 func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { 53 filename := common.HostProc("net/dev") 54 return IOCountersByFileWithContext(ctx, pernic, filename) 55 } 56 57 func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { 58 return IOCountersByFileWithContext(context.Background(), pernic, filename) 59 } 60 61 func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) { 62 lines, err := common.ReadLines(filename) 63 if err != nil { 64 return nil, err 65 } 66 67 parts := make([]string, 2) 68 69 statlen := len(lines) - 1 70 71 ret := make([]IOCountersStat, 0, statlen) 72 73 for _, line := range lines[2:] { 74 separatorPos := strings.LastIndex(line, ":") 75 if separatorPos == -1 { 76 continue 77 } 78 parts[0] = line[0:separatorPos] 79 parts[1] = line[separatorPos+1:] 80 81 interfaceName := strings.TrimSpace(parts[0]) 82 if interfaceName == "" { 83 continue 84 } 85 86 fields := strings.Fields(strings.TrimSpace(parts[1])) 87 bytesRecv, err := strconv.ParseUint(fields[0], 10, 64) 88 if err != nil { 89 return ret, err 90 } 91 packetsRecv, err := strconv.ParseUint(fields[1], 10, 64) 92 if err != nil { 93 return ret, err 94 } 95 errIn, err := strconv.ParseUint(fields[2], 10, 64) 96 if err != nil { 97 return ret, err 98 } 99 dropIn, err := strconv.ParseUint(fields[3], 10, 64) 100 if err != nil { 101 return ret, err 102 } 103 fifoIn, err := strconv.ParseUint(fields[4], 10, 64) 104 if err != nil { 105 return ret, err 106 } 107 bytesSent, err := strconv.ParseUint(fields[8], 10, 64) 108 if err != nil { 109 return ret, err 110 } 111 packetsSent, err := strconv.ParseUint(fields[9], 10, 64) 112 if err != nil { 113 return ret, err 114 } 115 errOut, err := strconv.ParseUint(fields[10], 10, 64) 116 if err != nil { 117 return ret, err 118 } 119 dropOut, err := strconv.ParseUint(fields[11], 10, 64) 120 if err != nil { 121 return ret, err 122 } 123 fifoOut, err := strconv.ParseUint(fields[12], 10, 64) 124 if err != nil { 125 return ret, err 126 } 127 128 nic := IOCountersStat{ 129 Name: interfaceName, 130 BytesRecv: bytesRecv, 131 PacketsRecv: packetsRecv, 132 Errin: errIn, 133 Dropin: dropIn, 134 Fifoin: fifoIn, 135 BytesSent: bytesSent, 136 PacketsSent: packetsSent, 137 Errout: errOut, 138 Dropout: dropOut, 139 Fifoout: fifoOut, 140 } 141 ret = append(ret, nic) 142 } 143 144 if !pernic { 145 return getIOCountersAll(ret) 146 } 147 148 return ret, nil 149 } 150 151 var netProtocols = []string{ 152 "ip", 153 "icmp", 154 "icmpmsg", 155 "tcp", 156 "udp", 157 "udplite", 158 } 159 160 // NetProtoCounters returns network statistics for the entire system 161 // If protocols is empty then all protocols are returned, otherwise 162 // just the protocols in the list are returned. 163 // Available protocols: 164 // [ip,icmp,icmpmsg,tcp,udp,udplite] 165 func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { 166 return ProtoCountersWithContext(context.Background(), protocols) 167 } 168 169 func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) { 170 if len(protocols) == 0 { 171 protocols = netProtocols 172 } 173 174 stats := make([]ProtoCountersStat, 0, len(protocols)) 175 protos := make(map[string]bool, len(protocols)) 176 for _, p := range protocols { 177 protos[p] = true 178 } 179 180 filename := common.HostProc("net/snmp") 181 lines, err := common.ReadLines(filename) 182 if err != nil { 183 return nil, err 184 } 185 186 linecount := len(lines) 187 for i := 0; i < linecount; i++ { 188 line := lines[i] 189 r := strings.IndexRune(line, ':') 190 if r == -1 { 191 return nil, errors.New(filename + " is not formatted correctly, expected ':'.") 192 } 193 proto := strings.ToLower(line[:r]) 194 if !protos[proto] { 195 // skip protocol and data line 196 i++ 197 continue 198 } 199 200 // Read header line 201 statNames := strings.Split(line[r+2:], " ") 202 203 // Read data line 204 i++ 205 statValues := strings.Split(lines[i][r+2:], " ") 206 if len(statNames) != len(statValues) { 207 return nil, errors.New(filename + " is not formatted correctly, expected same number of columns.") 208 } 209 stat := ProtoCountersStat{ 210 Protocol: proto, 211 Stats: make(map[string]int64, len(statNames)), 212 } 213 for j := range statNames { 214 value, err := strconv.ParseInt(statValues[j], 10, 64) 215 if err != nil { 216 return nil, err 217 } 218 stat.Stats[statNames[j]] = value 219 } 220 stats = append(stats, stat) 221 } 222 return stats, nil 223 } 224 225 // NetFilterCounters returns iptables conntrack statistics 226 // the currently in use conntrack count and the max. 227 // If the file does not exist or is invalid it will return nil. 228 func FilterCounters() ([]FilterStat, error) { 229 return FilterCountersWithContext(context.Background()) 230 } 231 232 func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { 233 countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count") 234 maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max") 235 236 count, err := common.ReadInts(countfile) 237 if err != nil { 238 return nil, err 239 } 240 stats := make([]FilterStat, 0, 1) 241 242 max, err := common.ReadInts(maxfile) 243 if err != nil { 244 return nil, err 245 } 246 247 payload := FilterStat{ 248 ConnTrackCount: count[0], 249 ConnTrackMax: max[0], 250 } 251 252 stats = append(stats, payload) 253 return stats, nil 254 } 255 256 // ConntrackStats returns more detailed info about the conntrack table 257 func ConntrackStats(percpu bool) ([]ConntrackStat, error) { 258 return ConntrackStatsWithContext(context.Background(), percpu) 259 } 260 261 // ConntrackStatsWithContext returns more detailed info about the conntrack table 262 func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { 263 return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu) 264 } 265 266 // conntrackStatsFromFile returns more detailed info about the conntrack table 267 // from `filename` 268 // If 'percpu' is false, the result will contain exactly one item with totals/summary 269 func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) { 270 lines, err := common.ReadLines(filename) 271 if err != nil { 272 return nil, err 273 } 274 275 statlist := NewConntrackStatList() 276 277 for _, line := range lines { 278 fields := strings.Fields(line) 279 if len(fields) == 17 && fields[0] != "entries" { 280 statlist.Append(NewConntrackStat( 281 common.HexToUint32(fields[ctENTRIES]), 282 common.HexToUint32(fields[ctSEARCHED]), 283 common.HexToUint32(fields[ctFOUND]), 284 common.HexToUint32(fields[ctNEW]), 285 common.HexToUint32(fields[ctINVALID]), 286 common.HexToUint32(fields[ctIGNORE]), 287 common.HexToUint32(fields[ctDELETE]), 288 common.HexToUint32(fields[ctDELETE_LIST]), 289 common.HexToUint32(fields[ctINSERT]), 290 common.HexToUint32(fields[ctINSERT_FAILED]), 291 common.HexToUint32(fields[ctDROP]), 292 common.HexToUint32(fields[ctEARLY_DROP]), 293 common.HexToUint32(fields[ctICMP_ERROR]), 294 common.HexToUint32(fields[CT_EXPEctNEW]), 295 common.HexToUint32(fields[ctEXPECT_CREATE]), 296 common.HexToUint32(fields[CT_EXPEctDELETE]), 297 common.HexToUint32(fields[ctSEARCH_RESTART]), 298 )) 299 } 300 } 301 302 if percpu { 303 return statlist.Items(), nil 304 } 305 return statlist.Summary(), nil 306 } 307 308 // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h 309 var tcpStatuses = map[string]string{ 310 "01": "ESTABLISHED", 311 "02": "SYN_SENT", 312 "03": "SYN_RECV", 313 "04": "FIN_WAIT1", 314 "05": "FIN_WAIT2", 315 "06": "TIME_WAIT", 316 "07": "CLOSE", 317 "08": "CLOSE_WAIT", 318 "09": "LAST_ACK", 319 "0A": "LISTEN", 320 "0B": "CLOSING", 321 } 322 323 type netConnectionKindType struct { 324 family uint32 325 sockType uint32 326 filename string 327 } 328 329 var kindTCP4 = netConnectionKindType{ 330 family: syscall.AF_INET, 331 sockType: syscall.SOCK_STREAM, 332 filename: "tcp", 333 } 334 335 var kindTCP6 = netConnectionKindType{ 336 family: syscall.AF_INET6, 337 sockType: syscall.SOCK_STREAM, 338 filename: "tcp6", 339 } 340 341 var kindUDP4 = netConnectionKindType{ 342 family: syscall.AF_INET, 343 sockType: syscall.SOCK_DGRAM, 344 filename: "udp", 345 } 346 347 var kindUDP6 = netConnectionKindType{ 348 family: syscall.AF_INET6, 349 sockType: syscall.SOCK_DGRAM, 350 filename: "udp6", 351 } 352 353 var kindUNIX = netConnectionKindType{ 354 family: syscall.AF_UNIX, 355 filename: "unix", 356 } 357 358 var netConnectionKindMap = map[string][]netConnectionKindType{ 359 "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX}, 360 "tcp": {kindTCP4, kindTCP6}, 361 "tcp4": {kindTCP4}, 362 "tcp6": {kindTCP6}, 363 "udp": {kindUDP4, kindUDP6}, 364 "udp4": {kindUDP4}, 365 "udp6": {kindUDP6}, 366 "unix": {kindUNIX}, 367 "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6}, 368 "inet4": {kindTCP4, kindUDP4}, 369 "inet6": {kindTCP6, kindUDP6}, 370 } 371 372 type inodeMap struct { 373 pid int32 374 fd uint32 375 } 376 377 type connTmp struct { 378 fd uint32 379 family uint32 380 sockType uint32 381 laddr Addr 382 raddr Addr 383 status string 384 pid int32 385 boundPid int32 386 path string 387 } 388 389 // Return a list of network connections opened. 390 func Connections(kind string) ([]ConnectionStat, error) { 391 return ConnectionsWithContext(context.Background(), kind) 392 } 393 394 func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { 395 return ConnectionsPidWithContext(ctx, kind, 0) 396 } 397 398 // Return a list of network connections opened returning at most `max` 399 // connections for each running process. 400 func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { 401 return ConnectionsMaxWithContext(context.Background(), kind, max) 402 } 403 404 func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { 405 return ConnectionsPidMaxWithContext(ctx, kind, 0, max) 406 } 407 408 // Return a list of network connections opened, omitting `Uids`. 409 // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be 410 // removed from the API in the future. 411 func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { 412 return ConnectionsWithoutUidsWithContext(context.Background(), kind) 413 } 414 415 func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { 416 return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) 417 } 418 419 func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) { 420 return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max) 421 } 422 423 // Return a list of network connections opened by a process. 424 func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { 425 return ConnectionsPidWithContext(context.Background(), kind, pid) 426 } 427 428 func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { 429 return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) 430 } 431 432 func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { 433 return ConnectionsPidMaxWithContext(ctx, kind, pid, 0) 434 } 435 436 func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { 437 return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0) 438 } 439 440 // Return up to `max` network connections opened by a process. 441 func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { 442 return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) 443 } 444 445 func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { 446 return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) 447 } 448 449 func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { 450 return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false) 451 } 452 453 func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { 454 return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true) 455 } 456 457 func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) { 458 tmap, ok := netConnectionKindMap[kind] 459 if !ok { 460 return nil, fmt.Errorf("invalid kind, %s", kind) 461 } 462 root := common.HostProc() 463 var err error 464 var inodes map[string][]inodeMap 465 if pid == 0 { 466 inodes, err = getProcInodesAllWithContext(ctx, root, max) 467 } else { 468 inodes, err = getProcInodes(root, pid, max) 469 if len(inodes) == 0 { 470 // no connection for the pid 471 return []ConnectionStat{}, nil 472 } 473 } 474 if err != nil { 475 return nil, fmt.Errorf("cound not get pid(s), %d: %w", pid, err) 476 } 477 return statsFromInodesWithContext(ctx, root, pid, tmap, inodes, skipUids) 478 } 479 480 func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) { 481 return statsFromInodesWithContext(context.Background(), root, pid, tmap, inodes, skipUids) 482 } 483 484 func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) { 485 dupCheckMap := make(map[string]struct{}) 486 var ret []ConnectionStat 487 488 var err error 489 for _, t := range tmap { 490 var path string 491 var connKey string 492 var ls []connTmp 493 if pid == 0 { 494 path = fmt.Sprintf("%s/net/%s", root, t.filename) 495 } else { 496 path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename) 497 } 498 switch t.family { 499 case syscall.AF_INET, syscall.AF_INET6: 500 ls, err = processInetWithContext(ctx, path, t, inodes, pid) 501 case syscall.AF_UNIX: 502 ls, err = processUnix(path, t, inodes, pid) 503 } 504 if err != nil { 505 return nil, err 506 } 507 for _, c := range ls { 508 // Build TCP key to id the connection uniquely 509 // socket type, src ip, src port, dst ip, dst port and state should be enough 510 // to prevent duplications. 511 connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status) 512 if _, ok := dupCheckMap[connKey]; ok { 513 continue 514 } 515 516 conn := ConnectionStat{ 517 Fd: c.fd, 518 Family: c.family, 519 Type: c.sockType, 520 Laddr: c.laddr, 521 Raddr: c.raddr, 522 Status: c.status, 523 Pid: c.pid, 524 } 525 if c.pid == 0 { 526 conn.Pid = c.boundPid 527 } else { 528 conn.Pid = c.pid 529 } 530 531 if !skipUids { 532 // fetch process owner Real, effective, saved set, and filesystem UIDs 533 proc := process{Pid: conn.Pid} 534 conn.Uids, _ = proc.getUids() 535 } 536 537 ret = append(ret, conn) 538 dupCheckMap[connKey] = struct{}{} 539 } 540 541 } 542 543 return ret, nil 544 } 545 546 // getProcInodes returns fd of the pid. 547 func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) { 548 ret := make(map[string][]inodeMap) 549 550 dir := fmt.Sprintf("%s/%d/fd", root, pid) 551 f, err := os.Open(dir) 552 if err != nil { 553 return ret, err 554 } 555 defer f.Close() 556 dirEntries, err := readDir(f, max) 557 if err != nil { 558 return ret, err 559 } 560 for _, dirEntry := range dirEntries { 561 inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, dirEntry.Name()) 562 563 inode, err := os.Readlink(inodePath) 564 if err != nil { 565 continue 566 } 567 if !strings.HasPrefix(inode, "socket:[") { 568 continue 569 } 570 // the process is using a socket 571 l := len(inode) 572 inode = inode[8 : l-1] 573 _, ok := ret[inode] 574 if !ok { 575 ret[inode] = make([]inodeMap, 0) 576 } 577 fd, err := strconv.Atoi(dirEntry.Name()) 578 if err != nil { 579 continue 580 } 581 582 i := inodeMap{ 583 pid: pid, 584 fd: uint32(fd), 585 } 586 ret[inode] = append(ret[inode], i) 587 } 588 return ret, nil 589 } 590 591 // Pids retunres all pids. 592 // Note: this is a copy of process_linux.Pids() 593 // FIXME: Import process occures import cycle. 594 // move to common made other platform breaking. Need consider. 595 func Pids() ([]int32, error) { 596 return PidsWithContext(context.Background()) 597 } 598 599 func PidsWithContext(ctx context.Context) ([]int32, error) { 600 var ret []int32 601 602 d, err := os.Open(common.HostProc()) 603 if err != nil { 604 return nil, err 605 } 606 defer d.Close() 607 608 fnames, err := d.Readdirnames(-1) 609 if err != nil { 610 return nil, err 611 } 612 for _, fname := range fnames { 613 pid, err := strconv.ParseInt(fname, 10, 32) 614 if err != nil { 615 // if not numeric name, just skip 616 continue 617 } 618 ret = append(ret, int32(pid)) 619 } 620 621 return ret, nil 622 } 623 624 // Note: the following is based off process_linux structs and methods 625 // we need these to fetch the owner of a process ID 626 // FIXME: Import process occures import cycle. 627 // see remarks on pids() 628 type process struct { 629 Pid int32 `json:"pid"` 630 uids []int32 631 } 632 633 // Uids returns user ids of the process as a slice of the int 634 func (p *process) getUids() ([]int32, error) { 635 err := p.fillFromStatus() 636 if err != nil { 637 return []int32{}, err 638 } 639 return p.uids, nil 640 } 641 642 // Get status from /proc/(pid)/status 643 func (p *process) fillFromStatus() error { 644 pid := p.Pid 645 statPath := common.HostProc(strconv.Itoa(int(pid)), "status") 646 contents, err := ioutil.ReadFile(statPath) 647 if err != nil { 648 return err 649 } 650 lines := strings.Split(string(contents), "\n") 651 for _, line := range lines { 652 tabParts := strings.SplitN(line, "\t", 2) 653 if len(tabParts) < 2 { 654 continue 655 } 656 value := tabParts[1] 657 switch strings.TrimRight(tabParts[0], ":") { 658 case "Uid": 659 p.uids = make([]int32, 0, 4) 660 for _, i := range strings.Split(value, "\t") { 661 v, err := strconv.ParseInt(i, 10, 32) 662 if err != nil { 663 return err 664 } 665 p.uids = append(p.uids, int32(v)) 666 } 667 } 668 } 669 return nil 670 } 671 672 func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) { 673 return getProcInodesAllWithContext(context.Background(), root, max) 674 } 675 676 func getProcInodesAllWithContext(ctx context.Context, root string, max int) (map[string][]inodeMap, error) { 677 pids, err := PidsWithContext(ctx) 678 if err != nil { 679 return nil, err 680 } 681 ret := make(map[string][]inodeMap) 682 683 for _, pid := range pids { 684 t, err := getProcInodes(root, pid, max) 685 if err != nil { 686 // skip if permission error or no longer exists 687 if os.IsPermission(err) || os.IsNotExist(err) || errors.Is(err, io.EOF) { 688 continue 689 } 690 return ret, err 691 } 692 if len(t) == 0 { 693 continue 694 } 695 // TODO: update ret. 696 ret = updateMap(ret, t) 697 } 698 return ret, nil 699 } 700 701 // decodeAddress decode addresse represents addr in proc/net/* 702 // ex: 703 // "0500000A:0016" -> "10.0.0.5", 22 704 // "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53 705 func decodeAddress(family uint32, src string) (Addr, error) { 706 return decodeAddressWithContext(context.Background(), family, src) 707 } 708 709 func decodeAddressWithContext(ctx context.Context, family uint32, src string) (Addr, error) { 710 t := strings.Split(src, ":") 711 if len(t) != 2 { 712 return Addr{}, fmt.Errorf("does not contain port, %s", src) 713 } 714 addr := t[0] 715 port, err := strconv.ParseUint(t[1], 16, 16) 716 if err != nil { 717 return Addr{}, fmt.Errorf("invalid port, %s", src) 718 } 719 decoded, err := hex.DecodeString(addr) 720 if err != nil { 721 return Addr{}, fmt.Errorf("decode error, %w", err) 722 } 723 var ip net.IP 724 725 if family == syscall.AF_INET { 726 if common.IsLittleEndian() { 727 ip = net.IP(ReverseWithContext(ctx, decoded)) 728 } else { 729 ip = net.IP(decoded) 730 } 731 } else { // IPv6 732 ip, err = parseIPv6HexStringWithContext(ctx, decoded) 733 if err != nil { 734 return Addr{}, err 735 } 736 } 737 return Addr{ 738 IP: ip.String(), 739 Port: uint32(port), 740 }, nil 741 } 742 743 // Reverse reverses array of bytes. 744 func Reverse(s []byte) []byte { 745 return ReverseWithContext(context.Background(), s) 746 } 747 748 func ReverseWithContext(ctx context.Context, s []byte) []byte { 749 for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 750 s[i], s[j] = s[j], s[i] 751 } 752 return s 753 } 754 755 // parseIPv6HexString parse array of bytes to IPv6 string 756 func parseIPv6HexString(src []byte) (net.IP, error) { 757 return parseIPv6HexStringWithContext(context.Background(), src) 758 } 759 760 func parseIPv6HexStringWithContext(ctx context.Context, src []byte) (net.IP, error) { 761 if len(src) != 16 { 762 return nil, fmt.Errorf("invalid IPv6 string") 763 } 764 765 buf := make([]byte, 0, 16) 766 for i := 0; i < len(src); i += 4 { 767 r := ReverseWithContext(ctx, src[i:i+4]) 768 buf = append(buf, r...) 769 } 770 return net.IP(buf), nil 771 } 772 773 func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { 774 return processInetWithContext(context.Background(), file, kind, inodes, filterPid) 775 } 776 777 func processInetWithContext(ctx context.Context, file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { 778 if strings.HasSuffix(file, "6") && !common.PathExists(file) { 779 // IPv6 not supported, return empty. 780 return []connTmp{}, nil 781 } 782 783 // Read the contents of the /proc file with a single read sys call. 784 // This minimizes duplicates in the returned connections 785 // For more info: 786 // https://github.com/shirou/gopsutil/pull/361 787 contents, err := ioutil.ReadFile(file) 788 if err != nil { 789 return nil, err 790 } 791 792 lines := bytes.Split(contents, []byte("\n")) 793 794 var ret []connTmp 795 // skip first line 796 for _, line := range lines[1:] { 797 l := strings.Fields(string(line)) 798 if len(l) < 10 { 799 continue 800 } 801 laddr := l[1] 802 raddr := l[2] 803 status := l[3] 804 inode := l[9] 805 pid := int32(0) 806 fd := uint32(0) 807 i, exists := inodes[inode] 808 if exists { 809 pid = i[0].pid 810 fd = i[0].fd 811 } 812 if filterPid > 0 && filterPid != pid { 813 continue 814 } 815 if kind.sockType == syscall.SOCK_STREAM { 816 status = tcpStatuses[status] 817 } else { 818 status = "NONE" 819 } 820 la, err := decodeAddressWithContext(ctx, kind.family, laddr) 821 if err != nil { 822 continue 823 } 824 ra, err := decodeAddressWithContext(ctx, kind.family, raddr) 825 if err != nil { 826 continue 827 } 828 829 ret = append(ret, connTmp{ 830 fd: fd, 831 family: kind.family, 832 sockType: kind.sockType, 833 laddr: la, 834 raddr: ra, 835 status: status, 836 pid: pid, 837 }) 838 } 839 840 return ret, nil 841 } 842 843 func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { 844 // Read the contents of the /proc file with a single read sys call. 845 // This minimizes duplicates in the returned connections 846 // For more info: 847 // https://github.com/shirou/gopsutil/pull/361 848 contents, err := ioutil.ReadFile(file) 849 if err != nil { 850 return nil, err 851 } 852 853 lines := bytes.Split(contents, []byte("\n")) 854 855 var ret []connTmp 856 // skip first line 857 for _, line := range lines[1:] { 858 tokens := strings.Fields(string(line)) 859 if len(tokens) < 6 { 860 continue 861 } 862 st, err := strconv.Atoi(tokens[4]) 863 if err != nil { 864 return nil, err 865 } 866 867 inode := tokens[6] 868 869 var pairs []inodeMap 870 pairs, exists := inodes[inode] 871 if !exists { 872 pairs = []inodeMap{ 873 {}, 874 } 875 } 876 for _, pair := range pairs { 877 if filterPid > 0 && filterPid != pair.pid { 878 continue 879 } 880 var path string 881 if len(tokens) == 8 { 882 path = tokens[len(tokens)-1] 883 } 884 ret = append(ret, connTmp{ 885 fd: pair.fd, 886 family: kind.family, 887 sockType: uint32(st), 888 laddr: Addr{ 889 IP: path, 890 }, 891 pid: pair.pid, 892 status: "NONE", 893 path: path, 894 }) 895 } 896 } 897 898 return ret, nil 899 } 900 901 func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap { 902 for key, value := range add { 903 a, exists := src[key] 904 if !exists { 905 src[key] = value 906 continue 907 } 908 src[key] = append(a, value...) 909 } 910 return src 911 }