diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d96945de8d0..b51338c91bb 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -84,6 +84,7 @@ https://github.com/elastic/beats/compare/v5.0.0-alpha5...master[Check the HEAD d *Packetbeat* - Add cassandra protocol analyzer to packetbeat. {pull}1959[1959] + - Match connections with IPv6 addresses to processes {pull}2254[2254] *Topbeat* diff --git a/packetbeat/procs/procs.go b/packetbeat/procs/procs.go index 4d8da54e202..3ba80b7321a 100644 --- a/packetbeat/procs/procs.go +++ b/packetbeat/procs/procs.go @@ -20,7 +20,7 @@ import ( ) type SocketInfo struct { - Src_ip, Dst_ip uint32 + Src_ip, Dst_ip net.IP Src_port, Dst_port uint16 Uid uint16 @@ -245,41 +245,74 @@ func (proc *ProcessesWatcher) FindProc(port uint16) (procname string) { return "" } -func hex_to_ip_port(str []byte) (uint32, uint16, error) { +func hex_to_ipv4(word string) (net.IP, error) { + ip, err := strconv.ParseInt(word, 16, 64) + if err != nil { + return nil, err + } + return net.IPv4(byte(ip), byte(ip>>8), byte(ip>>16), byte(ip>>24)), nil +} + +func hex_to_ipv6(word string) (net.IP, error) { + p := make(net.IP, net.IPv6len) + for i := 0; i < 4; i++ { + part, err := strconv.ParseInt(word[i*8:(i+1)*8], 16, 32) + if err != nil { + return nil, err + } + p[i*4] = byte(part) + p[i*4+1] = byte(part >> 8) + p[i*4+2] = byte(part >> 16) + p[i*4+3] = byte(part >> 24) + } + return p, nil +} + +func hex_to_ip(word string, ipv6 bool) (net.IP, error) { + if ipv6 { + return hex_to_ipv6(word) + } else { + return hex_to_ipv4(word) + } +} + +func hex_to_ip_port(str []byte, ipv6 bool) (net.IP, uint16, error) { words := bytes.Split(str, []byte(":")) if len(words) < 2 { - return 0, 0, errors.New("Didn't find ':' as a separator") + return nil, 0, errors.New("Didn't find ':' as a separator") } - ip, err := strconv.ParseInt(string(words[0]), 16, 64) + ip, err := hex_to_ip(string(words[0]), ipv6) if err != nil { - return 0, 0, err + return nil, 0, err } port, err := strconv.ParseInt(string(words[1]), 16, 32) if err != nil { - return 0, 0, err + return nil, 0, err } - return uint32(ip), uint16(port), nil + return ip, uint16(port), nil } func (proc *ProcessesWatcher) UpdateMap() { logp.Debug("procs", "UpdateMap()") - file, err := os.Open("/proc/net/tcp") + ipv4socks, err := sockets_From_Proc("/proc/net/tcp", false) if err != nil { - logp.Err("Open: %s", err) + logp.Err("Parse_Proc_Net_Tcp: %s", err) return } - defer file.Close() - socks, err := Parse_Proc_Net_Tcp(file) + ipv6socks, err := sockets_From_Proc("/proc/net/tcp6", true) if err != nil { - logp.Err("Parse_Proc_Net_Tcp: %s", err) + logp.Err("Parse_Proc_Net_Tcp ipv6: %s", err) return } socks_map := map[int64]*SocketInfo{} - for _, s := range socks { + for _, s := range ipv4socks { + socks_map[s.Inode] = s + } + for _, s := range ipv6socks { socks_map[s.Inode] = s } @@ -303,8 +336,17 @@ func (proc *ProcessesWatcher) UpdateMap() { } +func sockets_From_Proc(filename string, ipv6 bool) ([]*SocketInfo, error) { + file, err := os.Open("/proc/net/tcp") + if err != nil { + return nil, err + } + defer file.Close() + return Parse_Proc_Net_Tcp(file, false) +} + // Parses the /proc/net/tcp file -func Parse_Proc_Net_Tcp(input io.Reader) ([]*SocketInfo, error) { +func Parse_Proc_Net_Tcp(input io.Reader, ipv6 bool) ([]*SocketInfo, error) { buf := bufio.NewReader(input) sockets := []*SocketInfo{} @@ -316,7 +358,6 @@ func Parse_Proc_Net_Tcp(input io.Reader) ([]*SocketInfo, error) { logp.Err("Error reading /proc/net/tcp: %s", err) return nil, err } - words := bytes.Fields(line) if len(words) < 10 || bytes.Equal(words[0], []byte("sl")) { logp.Debug("procs", "Less then 10 words (%d) or starting with 'sl': %s", len(words), words) @@ -326,13 +367,13 @@ func Parse_Proc_Net_Tcp(input io.Reader) ([]*SocketInfo, error) { var sock SocketInfo var err_ error - sock.Src_ip, sock.Src_port, err_ = hex_to_ip_port(words[1]) + sock.Src_ip, sock.Src_port, err_ = hex_to_ip_port(words[1], ipv6) if err_ != nil { logp.Debug("procs", "Error parsing IP and port: %s", err_) continue } - sock.Dst_ip, sock.Dst_port, err_ = hex_to_ip_port(words[2]) + sock.Dst_ip, sock.Dst_port, err_ = hex_to_ip_port(words[2], ipv6) if err_ != nil { logp.Debug("procs", "Error parsing IP and port: %s", err_) continue diff --git a/packetbeat/procs/procs_test.go b/packetbeat/procs/procs_test.go index d1c36c7b15f..fef28f9c1ac 100644 --- a/packetbeat/procs/procs_test.go +++ b/packetbeat/procs/procs_test.go @@ -206,3 +206,44 @@ func TestFindSocketsOfPid(t *testing.T) { AssertInt64ArraysAreEqual(t, []int64{7619, 7620}, inodes) } + +func TestParse_Proc_Net_Tcp(t *testing.T) { + file, err := os.Open("../tests/files/proc_net_tcp.txt") + if err != nil { + t.Fatalf("Opening ../tests/files/proc_net_tcp.txt: %s", err) + } + socketInfo, err := Parse_Proc_Net_Tcp(file, false) + if err != nil { + t.Fatalf("Parse_Proc_Net_Tcp: %s", err) + } + if len(socketInfo) != 32 { + t.Error("expected socket information on 32 sockets but got", len(socketInfo)) + } + if socketInfo[31].Src_ip.String() != "192.168.2.243" { + t.Error("Failed to parse source IP address 192.168.2.243") + } + if socketInfo[31].Src_port != 41622 { + t.Error("Failed to parse source port 41622") + } +} + +func TestParse_Proc_Net_Tcp6(t *testing.T) { + file, err := os.Open("../tests/files/proc_net_tcp6.txt") + if err != nil { + t.Fatalf("Opening ../tests/files/proc_net_tcp6.txt: %s", err) + } + socketInfo, err := Parse_Proc_Net_Tcp(file, true) + if err != nil { + t.Fatalf("Parse_Proc_Net_Tcp: %s", err) + } + if len(socketInfo) != 6 { + t.Error("expected socket information on 6 sockets but got", len(socketInfo)) + } + if socketInfo[5].Src_ip.String() != "::" { + t.Error("Failed to parse source IP address ::, got instead", socketInfo[5].Src_ip.String()) + } + // TODO add an example of a 'real' IPv6 address + if socketInfo[5].Src_port != 59497 { + t.Error("Failed to parse source port 59497, got instead", socketInfo[5].Src_port) + } +} diff --git a/packetbeat/tests/files/proc_net_tcp.txt b/packetbeat/tests/files/proc_net_tcp.txt new file mode 100644 index 00000000000..8bf24684c0c --- /dev/null +++ b/packetbeat/tests/files/proc_net_tcp.txt @@ -0,0 +1,33 @@ + sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10676 1 ffff880417a2f800 100 0 0 10 0 + 1: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 3465487 1 ffff8803808e6040 100 0 0 10 0 + 2: 0100007F:1538 00000000:0000 0A 00000000:00000000 00:00000000 00000000 131 0 25937 1 ffff8800369d7040 100 0 0 10 0 + 3: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 23924 1 ffff88041a8bc800 100 0 0 10 0 + 4: 00000000:445C 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 33808 1 ffff88040100b800 100 0 0 10 0 + 5: 0100007F:44C0 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 23374 1 ffff88041aa83800 100 0 0 10 0 + 6: 0100007F:14E1 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 26012 1 ffff880036a98800 100 0 0 10 0 + 7: 0100007F:44C3 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 23377 1 ffff88030c6b4800 100 0 0 10 0 + 8: 00000000:91A3 00000000:0000 0A 00000000:00000000 00:00000000 00000000 104 0 15877 1 ffff8800369d7800 100 0 0 10 0 + 9: 0100007F:0CEA 00000000:0000 0A 00000000:00000000 00:00000000 00000000 118 0 22036 1 ffff88041ab6b800 100 0 0 10 0 + 10: F302A8C0:BBE4 78AB32B8:01BB 08 00000000:00000001 02:000008C1 00000000 1000 0 3778421 2 ffff880036a98040 21 4 28 10 -1 + 11: F302A8C0:E5AA E7C6176B:01BB 01 00000000:00000000 02:00000E81 00000000 1000 0 3694314 3 ffff880308728040 41 4 30 7 7 + 12: F302A8C0:BEE0 03117DA2:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 3752770 1 ffff88018d1c1040 28 4 33 10 -1 + 13: F302A8C0:9FCC 8E6ACE17:01BB 01 00000000:00000000 02:00000A0E 00000000 1000 0 3774260 2 ffff88019bc46040 21 4 14 10 -1 + 14: F302A8C0:B944 349C10C7:01BB 01 00000000:00000000 02:00000D1B 00000000 1000 0 3772933 2 ffff8804185a4800 30 4 0 10 -1 + 15: F302A8C0:9FE6 8E6ACE17:01BB 01 00000000:00000000 02:00000A68 00000000 1000 0 3778418 2 ffff880369b75800 22 4 18 10 -1 + 16: F302A8C0:9FE0 8E6ACE17:01BB 01 00000000:00000000 02:000008C1 00000000 1000 0 3778415 2 ffff8804185a4040 21 4 18 10 -1 + 17: F302A8C0:9FD0 8E6ACE17:01BB 01 00000000:00000000 02:00000A68 00000000 1000 0 3774286 2 ffff880127afc040 23 4 18 10 -1 + 18: F302A8C0:9FE2 8E6ACE17:01BB 01 00000000:00000000 02:000008C1 00000000 1000 0 3778416 2 ffff880156a29800 21 4 20 10 -1 + 19: F302A8C0:852E 98066B0D:01BB 01 00000000:00000000 02:00000681 00000000 1000 0 3460462 2 ffff88008ab32040 22 4 28 10 -1 + 20: F302A8C0:8450 214A3A2D:01BB 08 00000000:00000020 00:00000000 00000000 1000 0 3484928 1 ffff88039461d800 28 4 24 10 -1 + 21: F302A8C0:DD6C C5D43AD8:01BB 01 00000000:00000000 02:000003AE 00000000 1000 0 3708534 2 ffff880156a1a040 21 4 23 7 7 + 22: F302A8C0:A12A CED43AD8:01BB 01 00000000:00000000 02:00001041 00000000 1000 0 3760233 2 ffff88019e589800 22 4 21 10 9 + 23: F302A8C0:AAA2 069C10C7:01BB 01 00000000:00000000 02:00000C74 00000000 1000 0 3776722 2 ffff88008ab32800 33 4 8 10 -1 + 24: F302A8C0:B224 19CEFCC6:01BB 01 00000000:00000000 02:00000134 00000000 1000 0 3670508 2 ffff88040100b040 28 4 19 10 -1 + 25: F302A8C0:9FDE 8E6ACE17:01BB 01 00000000:00000000 02:00000A68 00000000 1000 0 3778414 2 ffff8801a6a4a040 22 4 19 10 -1 + 26: F302A8C0:BB1C BC887D4A:01BB 01 00000000:00000000 02:00000EB4 00000000 1000 0 3459085 2 ffff8801a9b57800 21 4 0 10 -1 + 27: F302A8C0:AC2C DB0DE636:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 3790176 1 ffff8800c57e2040 21 4 24 10 -1 + 28: F302A8C0:C53E C1ACA06C:01BB 08 00000000:00000020 00:00000000 00000000 1000 0 3709084 1 ffff88041989e040 65 4 24 10 -1 + 29: F302A8C0:9812 50020534:01BB 08 00000000:00000001 00:00000000 00000000 1000 0 3725685 1 ffff8803f32db800 29 4 24 14 -1 + 30: F302A8C0:9FE4 8E6ACE17:01BB 01 00000000:00000000 02:00000A68 00000000 1000 0 3778417 2 ffff8801a6a4a800 23 4 18 10 -1 + 31: F302A8C0:A296 CED43AD8:01BB 06 00000000:00000000 03:0000085B 00000000 0 0 0 3 ffff8802c3d61ef0 diff --git a/packetbeat/tests/files/proc_net_tcp6.txt b/packetbeat/tests/files/proc_net_tcp6.txt new file mode 100644 index 00000000000..2489a8aaa1f --- /dev/null +++ b/packetbeat/tests/files/proc_net_tcp6.txt @@ -0,0 +1,7 @@ + sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000000000000000000000000000:006F 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 10679 1 ffff880419b29140 100 0 0 10 0 + 1: 00000000000000000000000001000000:0277 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 3465486 1 ffff88041a77aa40 100 0 0 10 0 + 2: 00000000000000000000000001000000:1538 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 131 0 25936 1 ffff8800c541c080 100 0 0 10 0 + 3: 00000000000000000000000001000000:0019 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 23925 1 ffff8800c541c900 100 0 0 10 0 + 4: 00000000000000000000000000000000:445C 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 33807 1 ffff88041a77a1c0 100 0 0 10 0 + 5: 00000000000000000000000000000000:E869 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 104 0 15881 1 ffff8800c541d180 100 0 0 10 0