From 1fb4ce728b8ad225a1eef087a5c0b35e73615418 Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Wed, 10 May 2017 18:31:53 +0200 Subject: [PATCH 1/4] vendor: update tcptracer-bpf This includes https://github.com/weaveworks/tcptracer-bpf/pull/39 --- .../tcptracer-bpf/pkg/tracer/event.go | 2 + .../tcptracer-bpf/pkg/tracer/event_common.go | 11 +++- .../pkg/tracer/tcptracer-ebpf.go | 4 +- .../tcptracer-bpf/pkg/tracer/tracer.go | 14 +++++ .../pkg/tracer/tracer_unsupported.go | 6 ++ .../weaveworks/tcptracer-bpf/tcptracer-bpf.c | 62 +++++++++++++++++++ .../weaveworks/tcptracer-bpf/tcptracer-bpf.h | 11 +++- .../weaveworks/tcptracer-bpf/tests/tracer.go | 37 +++++++++-- vendor/manifest | 2 +- 9 files changed, 136 insertions(+), 13 deletions(-) diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event.go b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event.go index e13237376f..cd874297b0 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event.go @@ -32,6 +32,7 @@ func tcpV4ToGo(data *[]byte) (ret TcpV4) { ret.SPort = uint16(eventC.sport) ret.DPort = uint16(eventC.dport) ret.NetNS = uint32(eventC.netns) + ret.Fd = uint32(eventC.fd) return } @@ -64,6 +65,7 @@ func tcpV6ToGo(data *[]byte) (ret TcpV6) { ret.SPort = uint16(eventC.sport) ret.DPort = uint16(eventC.dport) ret.NetNS = uint32(eventC.netns) + ret.Fd = uint32(eventC.fd) return } diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event_common.go b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event_common.go index 96c2efb613..a47b3242eb 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event_common.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/event_common.go @@ -8,9 +8,10 @@ type EventType uint32 // These constants should be in sync with the equivalent definitions in the ebpf program. const ( - EventConnect EventType = 1 - EventAccept = 2 - EventClose = 3 + EventConnect EventType = 1 + EventAccept = 2 + EventClose = 3 + EventFdInstall = 4 ) func (e EventType) String() string { @@ -21,6 +22,8 @@ func (e EventType) String() string { return "accept" case EventClose: return "close" + case EventFdInstall: + return "fdinstall" default: return "unknown" } @@ -38,6 +41,7 @@ type TcpV4 struct { SPort uint16 // Local TCP port DPort uint16 // Remote TCP port NetNS uint32 // Network namespace ID (as in /proc/$pid/ns/net) + Fd uint32 // File descriptor for fd_install events } // TcpV6 represents a TCP event (connect, accept or close) on IPv6 @@ -52,4 +56,5 @@ type TcpV6 struct { SPort uint16 // Local TCP port DPort uint16 // Remote TCP port NetNS uint32 // Network namespace ID (as in /proc/$pid/ns/net) + Fd uint32 // File descriptor for fd_install events } diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tcptracer-ebpf.go b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tcptracer-ebpf.go index 288a744c53..8ead42277c 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tcptracer-ebpf.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tcptracer-ebpf.go @@ -68,7 +68,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _tcptracerEbpfO = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x5c\x01\x6c\x5b\xc7\x79\xbe\x47\x8a\x26\x25\xcf\x93\xe6\x96\x09\xcd\xa1\x98\xd2\x05\x89\x26\xa4\x29\x65\x4b\x32\xab\x36\x9d\xea\x36\xa9\xa6\x19\x95\x96\x89\xad\x60\x2c\xa5\x18\x96\xb6\x14\x3a\x36\x25\xd2\x96\x5e\x98\x6d\x2e\x8a\x64\x02\x91\x05\xb2\x93\x16\x42\x96\x66\xa2\x2c\xc7\xea\x9a\x0d\x1a\xb6\xc1\x01\xba\x81\x5a\x97\x61\xc2\x16\x0c\xc6\x5a\x0c\x1a\xea\x01\x42\x97\x75\xda\x86\xa2\xda\x9a\x79\x5a\x6a\x84\x03\xef\xff\x1e\xdf\x7b\xff\xbb\xf7\x48\xd9\x69\x4b\x20\x3d\xff\xdf\xbb\xff\xfe\xff\xee\xfe\xfb\xef\xbb\x7b\x4f\xfd\xed\x87\x8f\x3f\xe2\xd3\x34\x61\xfc\x34\xf1\xbf\xc2\x94\xcc\x5f\xe4\x13\xe6\xbf\x07\xf1\xbf\x3f\x2f\x34\x51\xb9\x8b\xb0\x67\x84\x10\x3f\x2b\x84\x28\xb6\xed\x54\x6b\xb2\x9e\xca\x49\xbc\x18\xdd\x95\x72\x65\x99\xea\x05\x7d\x42\xec\x54\xab\xd5\xca\x65\xc8\x7e\x21\x76\xab\xd5\x6a\x84\x19\xbd\xd6\x62\xb6\xeb\xab\xc9\xc0\xbf\x8c\x52\xbf\x7b\x94\xd9\x1d\x92\x76\xae\xa1\x9d\x62\x74\xd0\x61\x77\x48\x61\xe7\x19\xd9\x67\x21\xc2\x62\xbf\x7c\xa2\xef\x23\xbc\x19\x3d\xbf\x10\xe2\x54\x50\x88\x4e\x21\xc4\x2c\xca\x44\xd0\xa7\x71\xfd\x41\x0f\xbb\x95\x20\xc9\xe1\xe0\x8f\xa8\x5f\xd3\x90\xb5\xff\x23\xb9\x8c\x7e\xf9\xd0\xaf\xee\x18\x8d\x6f\x1e\xf5\xfc\x9f\x91\x7e\x24\xfc\x3f\x94\xed\xe9\x33\x21\x89\x3f\x76\xf0\xbf\x68\x1c\x5b\xa1\xf7\xd2\x76\x95\xca\x2d\x94\x9b\x28\xaf\xa3\xdc\x40\xb9\x8e\xf2\x75\x94\x6b\x28\x57\x51\x2e\xa1\x5c\xac\xfb\xe5\x93\x7e\x4d\x12\x1e\xcd\x91\x7f\xd3\x1d\x98\x87\x39\x92\x67\x22\xf0\xbf\x8b\xea\x75\x5f\x00\xde\x09\xbc\x13\xf8\x3c\xe1\xb9\x2e\xc2\xdb\x16\x48\x3e\x17\x23\xf9\x04\xd9\xd5\x0b\x71\x1a\x9f\x39\x8a\xc6\x62\x09\xfe\x0d\x93\x7f\xfa\xdc\x10\x3d\x9f\x19\x45\xfb\xe8\x4f\x69\xcd\xd6\xcf\xd4\x4c\x41\x3e\x4f\x77\xdf\x80\x3f\x73\x90\x69\x9c\x2a\xbf\x44\xe3\x37\xdb\x4a\xf3\x9b\x7d\xe9\x2d\x89\xcf\xfa\x84\xa8\x79\x94\xed\xfe\x1e\xda\xc9\x41\x8f\xc6\x75\x6a\xe6\x82\x94\xb3\xdd\xdb\x78\xfe\x05\x29\x9f\xf2\x51\x3b\x29\x7d\x42\xca\x23\x97\xa8\xfd\x94\x3e\x49\x65\xfe\x34\xd5\xf3\x53\xbd\x91\x17\x30\x7f\x03\x98\xa7\x6e\x9a\xa7\x8c\x96\x92\xe3\x1e\xd6\x9e\x46\xbc\x5c\xa2\x38\xd6\x34\x89\x07\xc4\x1f\xd3\xfc\x23\xee\xb2\xd1\x47\xa5\x5e\xfb\xb3\x24\x57\xca\x88\x4f\x4d\x88\x47\xab\xd5\xaa\x31\x8f\x95\xb4\x19\x9f\xb5\x25\x38\x55\x26\xbd\x6c\x94\xfa\x11\x10\xaf\xa0\x9f\xe3\x55\x2a\x6f\xd1\xf3\xee\x31\x94\x37\xe1\xe7\x24\x9e\xef\x22\x1e\x68\xfe\x8d\xf6\xdb\xdf\xe7\xf4\x63\x1c\x7e\xb4\x58\xec\xeb\xd3\x9d\x0d\xf5\x6e\x29\xf5\xba\x1a\xea\x8d\x59\xfa\x6d\xea\xc5\x1a\xea\xdd\x54\xea\x51\x3c\xb6\xdf\xe5\xac\x3f\x89\xfa\x21\xc5\xf8\xea\xd3\x14\xbf\xfa\x65\x1a\x2f\x95\xbd\x5d\x45\xff\x52\x2b\x18\xff\x61\xca\x07\xa9\xcb\x34\x0f\xe9\x81\x5e\x8a\xbf\x65\x63\x3e\xe2\x24\x97\x6f\x62\x1e\x07\xa8\xfe\x15\x9a\x97\xf4\x89\x8f\xc9\x32\xdc\x92\xb5\xc5\xdd\x2c\xca\xb0\x9f\xe2\xf2\x73\x3e\x21\xaa\x55\x21\xc2\xbe\x93\x24\x6b\x90\x35\x8a\x6b\x23\x4f\x15\xa3\x93\x2c\x0f\xcf\xd9\xf2\x82\xb5\x5f\x39\xf4\xab\x03\xfd\xea\x60\x79\x33\xc6\xf6\x89\x49\x45\x1e\x0d\x88\x47\x65\x19\xd6\xee\x97\x79\x30\xac\x3d\x24\xc7\xa7\xb6\x0e\x02\xf2\x39\xad\x7f\xc3\x9f\x74\x74\x0c\xfe\xd0\xf8\xb5\x3f\xe2\x1e\x87\xde\xf3\x35\xee\x3a\x5f\x63\x8a\xf9\x3a\x25\x30\xae\x28\x6b\xfd\xf8\xef\x6a\xb5\x6a\xf4\xe3\xb1\x8e\x9e\xba\x9f\x9a\xf4\x6f\x1d\xeb\x2d\xc6\xfc\xa7\x79\xd6\xcb\x94\x47\x55\xf1\x66\x5d\x0f\xdc\xff\x54\x19\x71\x12\xbd\x81\xf6\xef\xb5\xb5\x9f\x8d\x52\x9c\xb4\xef\x73\xb6\x7b\xd3\x33\x4f\x18\xf1\xf5\x16\xda\x3d\xc4\xfc\x46\x1e\x28\x77\xba\xfa\xbd\xeb\xe9\x37\xe2\x35\xba\x85\xf6\x0f\x30\xbf\x4f\x52\xbb\x9f\x75\xb6\x7b\xd2\xd3\xef\x93\xf0\xfb\x7b\x68\x97\x36\xda\x54\x79\x0c\xf6\x36\x1d\x3c\xa2\x99\xb8\x6c\xc4\x5f\x7e\x6a\xbc\x29\xc2\x79\xd3\x28\x5b\xaf\x43\x0e\xbb\xa3\x5e\xfc\x05\x71\x12\xde\x77\x4b\x34\xab\x57\x5b\xa7\xfa\x49\xe1\xa8\xef\xc5\xcf\x4c\x9e\xf4\x03\xea\x47\x9d\x27\x7d\x9f\xe4\x65\xde\x8f\xce\xaa\xbd\xde\xa3\xc4\x93\xb4\x7f\x23\x9e\x34\x0d\x9e\x74\xcf\xbf\x0a\xbb\x1e\x78\x51\x14\xfb\x6d\x14\xbc\x08\xeb\xb1\x18\x05\x8f\x88\x82\x47\x44\xc1\x3b\xa2\xe0\x45\xd1\x45\x94\x0b\x28\x89\xcf\x5c\xf3\x83\x27\x0d\x8c\x63\x7f\x9c\x00\xdf\x20\x9e\xa4\xe7\xc1\x8f\x06\x72\xb6\xfd\x53\xcf\xd3\xfe\xa7\x17\xc0\x87\x86\x89\x37\x15\x07\xc0\xab\xf2\x31\x3c\x8f\xe3\x39\xec\x0e\x80\x47\xe5\x91\xaf\x0a\x43\x78\x0e\x3f\x07\xc0\xa3\xf2\xe0\x47\x03\xab\xb6\x7e\xa5\x0a\xe0\x45\xc3\x6f\x82\x6f\x81\x17\x0d\xd3\xb8\x54\x1e\xa6\xf1\x9a\x0d\x80\x17\x9d\xf8\x36\xf1\xa2\x16\xf0\xa2\xe1\x7f\x44\x3b\xe0\x45\xc3\x34\x8e\x53\x05\xf0\xa2\xe1\x4d\x3c\x07\xdf\x69\x01\x2f\x3a\x47\xfb\xcd\xc8\x18\xe6\x63\x18\x3c\xad\x40\x7c\x29\x75\x0e\x3c\x0a\x76\x47\xe0\x47\xf1\x84\x31\x2f\x71\x94\xb4\x2f\x26\x7c\x5f\x13\xb5\x10\xad\x5c\x45\x9c\x05\x85\x78\xbd\x5a\xad\xb6\xf7\x23\xfe\x2c\x79\x22\x66\xd9\x8f\x1c\x79\xbf\x8c\x76\xef\xa6\xb8\xd1\xcb\x31\xc8\x68\xc7\x12\xc7\x9d\x6c\x9d\x8e\x2b\xf7\xad\x17\xa9\x1f\x21\xec\x0b\x28\x13\xa1\x17\x58\x3c\xc2\x0e\xe2\xb9\x18\xdd\x41\xb9\x8d\xbc\xf5\x0e\xca\x5d\xe0\x5b\xc8\x5b\xd8\x27\xa6\x69\x7e\x39\x9f\xa9\xf9\xd5\xe1\xd2\xff\x90\x6d\xbf\x73\xd7\x8f\x30\xfd\xce\x3d\xe8\x5b\xf5\x76\xf6\x68\x37\xc4\xf4\xb7\x95\xfa\x8d\xf9\xdf\x3b\xb7\xc9\xff\x76\xf7\xc8\xff\xb6\x9a\xe2\x13\x5b\xae\x7c\x42\xc9\x6f\xbf\x86\xb8\x78\x89\xf2\x89\xfe\x2a\xe2\xe3\x09\xca\x2b\x95\x14\xe9\x8f\x5c\xa4\x52\xbf\x8a\xb8\x79\x0a\x79\x65\x15\xe7\xc1\x12\xe5\x9b\xa9\x15\xc4\xd1\xf0\x1c\xf8\xe3\x2e\xf8\xe3\x53\x8c\x5f\x3e\x4d\x7c\x51\xfb\x38\xf9\xf9\x45\xd8\xa9\xe7\xd9\x8f\x12\x8e\xfb\x02\x93\x27\xd2\xf8\x54\xee\xe1\x78\x9f\x94\x8b\xc3\x21\xe4\xa5\x0e\xe4\xbf\x08\xcb\xe7\x83\xb6\xf5\xad\x97\x47\x6d\xeb\xdc\x3a\x5e\x71\x05\xaf\x4c\xb4\xd2\x46\x65\x8c\xcb\x29\x0d\xeb\x0e\x65\x44\x6e\xfb\x26\x1f\xfb\xc0\x3d\xb4\xe1\x24\x82\x87\x6c\xfd\x6c\xa4\xf7\xd8\x3d\x34\x81\xb3\xfb\xe8\x79\xfa\x09\x9a\x17\x63\x7d\xa7\x4b\xf7\xc2\x7f\xea\xef\x0d\x8d\xfc\xcb\x46\xbb\x80\x47\x18\xde\x0d\xbc\x03\xeb\xfa\x01\xc7\xfe\xcc\xf3\x4d\x4c\x99\x6f\xd0\x7f\x8b\xde\x78\x13\x7a\x7b\xe1\x31\x21\xc9\x27\x06\xd9\xbc\x51\x9e\xd7\x4f\x51\x5e\xd7\x27\x73\x0e\x3f\x36\xbd\xf6\x7d\xdc\x5f\x84\x5b\xaf\x90\x3e\xe2\x39\xa1\x5d\x96\xfb\x79\x65\x5a\x3d\x2f\x35\xfe\x1f\x94\xf5\x5e\x91\xed\x70\x7e\x93\xc5\x3e\xd7\xfc\xfd\x10\xf1\x42\x1d\xcf\xc3\x5a\x9b\xb4\x7f\xe7\xed\x96\x30\x3f\xbf\x83\xf6\x69\x9f\xae\x7c\x89\xea\xa9\xf2\xc1\x9a\x22\xff\x4c\x95\xc9\x6e\x42\xfb\x7e\xd5\x67\xe3\xdd\x6b\x88\x9b\x1d\xf8\xb7\x85\xf2\x56\xd5\xea\x77\x1a\xfb\x89\x7e\x31\xd2\xb4\x7d\x5b\x3e\xba\xd8\xd9\x50\x6f\x47\xa9\xd7\xd5\x50\x6f\x4b\x95\x6f\x2f\xc6\x1a\xea\xdd\x52\xea\x21\x4f\x47\x9c\xf5\x87\xac\x79\xfa\x4b\xe6\x3c\x91\x9e\x91\xa7\x87\x5c\xf3\xf4\xb6\xf2\x9c\x8e\xf1\x1f\x5e\x44\x1e\xdd\x41\x1e\x7d\x19\x79\x16\xf3\xd1\x4d\xbc\x6c\x0a\xe7\xb3\x6c\x74\x99\xea\x5f\xa1\x79\x49\x9f\x58\xa1\xbc\xdb\xf2\x29\x69\xcf\x79\x4e\xa7\x84\x6b\x9e\xd3\x7f\x99\xe4\x7a\x9e\x7d\x88\xf4\x1c\xf7\x95\xb4\x4e\x02\x82\xce\x9f\xd6\xb8\x5d\xf4\x8c\xdb\x23\x52\x36\xe3\x5f\xcd\x8f\x55\x71\x23\x79\x83\xdf\x3e\xbe\xcf\x48\x1f\xcc\x7b\x5c\xfd\x32\xe5\x11\xde\xcf\xe2\x80\xc9\xf3\x35\x19\xb7\xb4\xff\xa5\xca\x7f\x8a\x38\xfe\x0d\xc8\x8b\x90\x73\x90\x5f\x86\x5c\x80\x4c\xe3\x5d\x39\x48\x7e\xdc\xc0\x7e\x91\xed\xa6\xfd\x6f\x16\xf9\xd7\xcc\xc3\xd8\x07\xcb\x2b\x68\xe7\x69\xe4\x65\xa3\xdf\xd8\x57\xcb\x46\xff\xb1\x0f\xaf\xa0\xff\x2d\x14\x5f\xc6\xfe\x13\xf1\xd9\xc7\xf5\x5a\x40\x88\x38\xc6\xe1\x50\x93\xf3\xe0\xb7\x9c\x57\xcd\xbc\xe4\xa3\xbc\x84\x7a\xf5\x73\xdf\x1d\xe7\x95\xff\xa9\xb6\x29\xf2\x9d\x79\xde\xd9\x23\x2f\xbd\x38\xaa\xf4\x47\xc5\x4b\x87\x54\xfc\xce\x43\x9f\xf3\xd2\xc5\x3d\xe8\x37\xe4\xa5\x1e\x76\x9b\xe2\xa5\x4d\xe4\x3b\x25\x2f\x6d\x22\xdf\x29\x79\xa9\x47\xbe\xdb\x6a\x2a\xdf\xed\x91\x97\xd6\xef\x01\x29\x6e\xf4\x2b\x88\x0f\x9c\xcf\x46\xc2\x88\x47\xe4\xc1\xe2\x00\xad\x1f\x7d\x05\x7c\xd4\x38\x27\x2e\x23\x7e\xba\xaf\x23\x2f\x82\x8f\x46\xff\x81\xd6\xe1\x12\xf8\x68\x1b\x9d\x3b\xc3\x81\x32\xb5\xdf\x4b\xed\x87\x5b\x7e\x5f\xca\x66\x3e\xfc\x2a\xc9\xf5\x7c\xf8\x7b\x94\x5f\x1c\xf9\x70\x9f\xcc\x87\xd6\xf5\xb7\xe6\xb9\xfe\x2e\x39\xf6\x7b\xb7\xfa\x94\x37\x9f\x63\xfc\x48\x7d\x7f\xa0\x8a\xdf\x1f\x4f\xde\x7c\xd9\x96\x37\x75\xac\x77\x7d\x19\x79\xac\xdb\x7e\x7f\x6b\xe4\x3b\x7d\x19\xf7\x22\x78\x5f\x53\x8c\xe2\x7d\x4e\xf9\xba\x4b\x5e\xa5\x7b\x09\xce\x97\x2b\xef\xa7\x32\x49\xd7\x90\xf2\xfe\x40\xe6\x5d\x94\xd9\xe1\x4b\xf5\x7c\x2c\xf9\x73\xf4\x45\xd8\x59\x65\x79\x77\xe9\xb6\xf3\xee\xa8\x25\xef\x06\x04\xf5\xcf\xe0\xb5\x9c\xc7\x9a\xf7\x66\xf1\xba\x2c\xd7\x4f\x08\x71\x3d\xb5\x47\x7e\x5b\xbf\xd7\x7a\x80\xf4\xc1\x67\x13\x5a\xb7\x8c\xaf\xe2\x13\x38\xdf\xe0\xbd\xcd\x35\xd8\x29\x96\x68\xfc\xd3\x25\x8a\x7f\xe3\xfc\x59\xc1\xfb\x22\xd5\x7a\xdd\x50\x9e\x93\x8d\x75\xbe\xe1\xba\xce\xbf\xad\x58\xe7\xc5\x12\xc5\x59\xb6\xb4\xe4\x88\x7f\xaf\xf7\x9d\x4e\x1e\xdd\x4e\x3c\x7e\x12\xe3\xf7\x6a\x4c\x79\xee\xcb\x46\xef\xd4\xce\xaf\x7b\xef\x8b\x1e\xe3\xb6\xa4\xdc\x17\xe1\x0f\xf3\x3b\xa1\x6d\x33\xfe\xbd\xd0\x80\x7f\x0f\xda\xf9\xb7\xf1\x9e\xcc\xc3\x9f\x05\xaf\xf7\x64\x1e\x7a\x4a\xfe\x6d\xdc\x93\x78\xe8\x29\xf9\xb7\x71\x4f\xe2\xa1\xa7\xe4\xdf\xc6\x3d\xc9\x21\x67\xfd\x41\xeb\x7e\xf4\x82\x39\x9f\xf6\x38\x1d\xdc\x23\xff\xc6\xf8\xe3\xde\xd3\xe4\xdf\x2b\x8c\x7f\xaf\x32\xfe\xfd\x07\x8c\x7f\xbf\x06\xfe\x4d\x0b\xd6\xc9\xbf\xe9\xa0\x6c\xee\x37\x14\x7f\xc6\x7e\x93\xd0\xfe\x9e\xfc\xb5\xc4\x6f\x2d\xae\x02\xe2\x2f\xdf\x93\x38\x4c\x68\x2b\x8c\x9f\x19\xf7\x25\x0b\xb7\xc7\xcf\x8c\xfb\x37\xe6\x8f\x8a\x9f\x0d\x7a\xdd\xdf\x29\xf4\x39\x3f\x5b\xd8\x83\x7e\xd3\xf7\x86\x0a\xbb\x7b\xba\x37\xf4\xb0\xeb\x79\x6f\xe8\xa1\xe7\x79\x6f\xa8\x58\x0f\x5b\x4d\xad\x87\x3d\xf2\xb3\x15\xc4\x85\xf1\x9d\xc4\x65\xe3\x7d\xc1\x2a\xf6\x75\xc4\x49\x37\x78\x40\x79\xdb\x76\xae\x9b\x5a\x42\xdc\xb4\xd1\x3e\x39\x85\xf7\xc8\xd9\x13\xdf\xa4\xf5\x72\x15\xbc\xec\xa9\x37\x28\x3f\x1e\x25\x7f\x46\x3e\x4d\x65\x38\x78\x8a\xfc\xc4\xbd\xf9\xc8\xfd\x06\x4e\xf7\xfa\x9f\x03\x3b\x08\x8b\xc7\x49\x0e\x40\x0e\xd0\xfd\x7f\xa2\x25\x20\x4b\xbe\xfe\x22\x72\xf9\x59\xee\xef\x3e\x49\xf7\x5d\x09\x1f\x6d\x9c\x8d\xef\xed\xbe\x58\x1f\xdf\x80\xe5\x1e\x8b\xdf\xcf\x17\x4b\xe6\x77\x46\x7e\xb9\x5e\x46\x31\x4e\x71\xc6\x53\xc6\xd8\x3e\x36\x81\x71\x34\x78\xd7\x6b\x58\x6f\x93\x90\xff\x1c\xf2\x69\x1b\xcf\x72\xf2\x28\xe2\x61\x91\xa0\xdd\x7f\xa3\x5e\x72\x86\xca\x59\xd4\x37\xf5\x66\x98\x7f\x05\x07\xcf\x1c\x57\xdc\x9b\xea\x65\x9c\x9f\x97\xc1\xff\xf0\x9d\x42\x05\x3c\x7b\x16\xef\x6d\x2a\x14\xfe\x8e\xf7\xdc\x15\xf0\xfd\xb0\xc0\xbe\x66\xc8\x2d\x44\xb0\x2a\xf4\xfa\xab\x3e\xef\x66\xfd\x83\xb6\xe7\xc9\x9c\xb0\xeb\x4b\x16\x66\xe7\x79\x83\x1e\x3c\xcf\x38\xdf\xf0\x73\xb6\xd1\x3f\xd7\x7e\x1d\xb4\xdb\x4d\x0f\x18\xfb\x09\xf1\xd0\x1b\x01\x8c\x2f\xbe\x4b\x72\x1f\xff\x97\xd8\xf8\xbf\xec\xe0\x33\x0b\x9e\xe7\x8c\xed\x77\x79\xfd\x25\xcf\xfa\xdf\x7d\x57\xfc\x44\xe3\x79\x89\xc5\xf3\x8a\x2d\x9e\x53\xe0\xeb\x6e\xf1\xec\xbc\x6f\x99\x61\xeb\xa4\xb9\x78\xbd\xd3\x78\x08\x88\x6f\xfc\x44\xc7\xcd\x38\xb7\xe8\xcb\x38\xcf\x60\x3c\xcc\x73\x0d\x3f\x8f\xe1\xfd\xaf\xf1\x5d\x9d\x6b\x9e\x98\x57\x9f\xb7\xb0\x4f\x24\x7f\x11\x71\x8a\xfd\xfc\x06\xca\xec\x40\x89\xf9\xff\xdc\x8f\x6d\xdc\x47\xe5\x18\xd3\x79\xd1\xfc\xbe\x71\xd7\x3e\x3e\x87\xec\xdf\x2b\x18\xdf\x1b\xf0\x73\x57\xb8\x95\xee\x3f\xad\xeb\x43\xf5\x7d\x84\xf3\xdc\x35\x20\x4b\xf3\xdc\x15\xa7\x73\x57\x09\xe7\x65\xb4\x9f\x2d\xe1\xfb\x96\xd2\xdb\xe0\x47\x8d\xf7\xfb\xb7\xf7\xc0\x13\x82\xf8\x3e\xa0\xd1\x77\x66\xc5\x12\xf8\x68\x69\xa7\xe9\x73\x9f\x9a\xe7\x18\xfc\x61\xdb\x95\x3f\xa8\xce\x0b\xc5\xd2\x22\xc6\x61\xc2\x91\xbf\xbc\xef\x8b\xf9\x79\xec\x00\x7b\x7f\x82\x76\xa3\x77\xda\xee\xaf\xde\xf6\x39\x6f\xa2\xd6\xdf\x10\xde\x73\x39\x78\xf6\x04\x78\xf6\xbf\x57\x7d\xb6\xfc\x80\xef\x28\xf0\x3e\x92\xf3\x8c\x62\xd4\x78\x4f\x89\xfc\x88\xef\x54\xcd\xf3\xa1\xf7\xf7\x25\xa9\x32\xce\x2b\x51\xfa\x0e\xc3\x7c\xaf\xf8\x5a\xd3\xe7\xc5\x75\x8f\xf3\xa2\x8a\x77\xbe\x61\xfd\x0e\x8b\xf1\x4e\xf3\x3b\x2f\xdc\xd3\x95\x69\x3d\x98\x79\xfb\x6f\x1d\xf9\x62\x4d\xc9\x2b\xec\x79\xcb\xf8\x2e\x76\x16\x65\xd8\xf7\x37\x96\xfc\x45\xbf\xda\x90\x7d\xe8\xe0\x1b\x18\x97\x0d\x9b\xbe\x79\xee\x5a\x57\xce\x03\xe5\x17\x4d\x7c\xe8\x20\xf1\x2c\x6b\xbe\xaa\xc5\x97\x5e\x1e\x77\xd8\x93\xe3\x7b\x99\xe6\x2f\x20\x9e\x7d\x8f\xe3\xe8\xeb\xf2\xbc\xf6\xde\xc5\x11\xbe\xeb\xde\xe3\xf7\x4b\x66\x7c\xbd\xe5\x12\x5f\x8d\xcf\x61\xd7\xef\xf0\x1c\xb6\xb9\x47\x7d\x7e\xfe\x5c\xf7\xd0\x37\xe2\x3b\xd8\xea\x3c\x77\x6e\x78\x9c\xaf\xcc\x38\xdf\x72\x89\xf3\xef\x36\x19\xe7\x1b\xd8\xbf\x69\xfc\x13\x3e\xbc\x87\x47\x7f\x1a\x9f\x63\x5a\x11\x87\x78\x8f\xbe\x42\xf3\x9a\x68\xa1\x83\x55\xa5\x0f\xed\xe0\x7b\x28\xe3\x7b\x2c\xca\x0a\x96\xf3\x4d\xa0\xa7\xde\x3f\x8a\x37\x7c\x7f\x76\x15\x71\xf5\x94\xf9\xdd\xae\xcf\xf2\xde\x2c\xfd\x44\x81\xf1\x17\x3b\xff\x48\x95\xdf\x44\xfc\xcc\x43\xbe\x01\xf9\x39\xc8\xc6\x7d\xd7\x82\x8d\xe7\x65\xa3\x97\x58\xfc\xbd\xe8\x18\xcf\x39\xe5\x78\xce\x37\xc8\x1b\x83\x2e\x79\xe3\x21\xd8\x5b\x70\xc9\x1b\x1f\x69\x90\x37\xfa\x1c\x79\x63\xc2\x23\x6f\x54\xf0\xf7\x19\x4e\x7e\x49\xdf\x6f\x8c\xe0\x3b\x98\xb0\x46\xdf\x77\x8c\x7c\x12\xb2\xbf\xcb\x36\x6e\x4e\x3f\xef\x6d\xe0\x67\xe7\x6d\xe5\x37\x93\x8f\xfd\x87\x94\x5b\x2c\xff\x09\x7c\x03\xa2\xc2\x34\xf0\xb0\x50\x13\x58\x87\x11\x97\x16\xac\x4b\x81\xd5\xf4\xe6\xf1\xef\xda\xef\xd3\xa3\xc7\xc5\xbb\x96\x6f\xa5\x55\xbf\xaf\xc8\xf7\x22\x01\xd1\xb1\xcf\x8e\x7f\x1c\xf8\x0e\xc3\xbf\xe5\x23\x7c\x35\x68\xc7\xbf\x0c\xbc\xcb\x6f\xc7\xa3\xc0\x47\x59\xfd\x1f\x6a\x84\x87\x42\x76\xfc\x2f\x80\xc7\x98\xdd\x67\xe1\xcf\x04\xab\x7f\x12\x78\x8e\xe1\x9f\x01\x7e\x81\xf9\xf3\x2d\xb4\xbf\xc4\xea\x5f\x01\xbe\xc6\xf0\xbf\x93\xfe\xb7\x8a\x18\xf3\xff\x20\xf0\x49\x86\xff\xa7\x46\xb8\x60\xed\xfc\x19\xf0\x18\xc3\x5f\xf5\x13\x3e\xc8\xf0\x37\x51\x7f\x81\xf9\xdf\x2f\xe5\xfd\xe2\x3a\xc3\x7f\xd7\x47\xf8\x78\xc0\x8e\xb7\x01\x9f\x67\xf8\xbf\x68\x84\x2f\x32\xfc\x0f\x81\xaf\x32\xfc\x4f\xd0\xce\x26\xf3\xf3\x37\x81\x87\x5a\xed\xf8\x2f\xc0\xcf\x55\x3b\x2c\x4e\xa2\x3e\xff\x63\xb9\x1f\xc1\xee\x28\xc3\x9f\x97\xed\x1c\x10\xe3\x2d\x76\xfc\x0c\xf0\xb9\x36\x3b\xfe\x59\xe0\x1b\x0c\xff\x2b\x1f\xe1\x9b\x9c\x0f\x03\xcf\xb1\xf6\x7f\x0b\xed\x2c\xb2\x76\x1e\x34\xda\x67\xf3\x7e\x0e\xed\xec\x32\xfc\x3c\xea\x47\x58\x3b\x9f\x07\x2e\x7e\xc6\x8e\x3f\x02\x7c\x67\xbf\x1d\xbf\xcf\xaf\xf6\x3f\x02\x3c\xc7\xf0\x15\xf8\x73\x81\xe1\x49\xe0\xdb\x0c\xff\x86\xc4\xdb\xc5\x2a\x1b\x87\xef\x68\x84\x77\xb2\x7e\xbd\xe2\x27\x9c\xc7\xff\xd7\xd1\x0e\x8f\x93\x27\x81\x6f\x31\xdc\x07\x7c\x87\xe1\x5f\x05\xbe\xcb\xfc\xfc\x6b\xf8\x33\xca\xf2\xf1\x2b\xc0\x27\x18\xfe\x8c\xdc\x37\x23\x82\xff\xee\x93\xf8\x21\x07\xfe\xb6\xdc\x37\xdf\xe7\xc0\x3f\x2a\xf1\xf7\x3b\xf0\xcf\xcb\x76\x0e\x38\xf0\x0f\x4b\x7c\xbf\x03\xff\xa6\xcc\xe7\x7e\x07\x7e\x42\xe2\x41\x07\x1e\x94\x78\xbb\x03\x5f\x95\x78\xc0\x81\x0f\x4a\xbc\xd5\x81\x6f\x49\xff\x7f\xce\x81\xdf\x2f\xf1\x83\x0e\x3c\x2c\xfd\xbf\xdb\x81\xb7\x49\x1e\x19\x76\xe0\xbf\x26\xdb\xb9\xcb\x81\x0f\xa1\xac\x4d\x4f\x8d\x79\xc5\x99\x9c\x63\xf2\x9a\x45\x3e\x26\x84\xd8\x0d\x98\x72\x6d\xab\x5f\x08\xd9\x9f\x5b\xdb\x8f\xb3\xf6\xe3\xac\xfd\x9a\xbc\xc4\xda\x9f\xf3\xdb\xe5\xed\xa0\xdd\x5e\xa7\xc5\xde\xa7\x98\xbd\x5a\xfd\xeb\x4c\xee\xd2\xec\xf2\x96\xdf\xde\x5e\xdc\xd2\x9f\xda\x5c\x4d\xb2\xfe\x6d\x33\x7b\x91\x56\xbb\xbc\xd6\x66\xca\x9f\x10\x02\x7f\xa5\x65\xda\x1b\x64\xf6\x57\x99\xbc\xde\x6a\xb7\x3f\xd7\x66\xb7\xbf\xda\x66\xb7\x77\x7d\xbf\xbd\xfe\xd0\x01\xbb\xfd\x71\x66\x7f\x83\xd9\xeb\xf4\xd9\xe5\xf9\x16\x7b\x7b\xeb\x21\x36\x1e\xad\xf6\xf6\xc5\x83\x85\xcc\x5c\x41\x64\x67\x32\x85\xdc\xcc\xd9\xc7\x33\xc9\xe4\xd4\x99\x4c\x21\x99\xce\x67\x93\xa9\x74\x3a\x93\x2b\x88\x07\x67\x32\xa7\xeb\x8f\x3f\xcc\x9f\x5a\x14\x0b\xe9\x5c\xf2\x7c\x7f\x32\x7d\xf6\xcc\x99\x4c\xba\x20\xb2\x6a\xd8\xde\x9c\xea\xa1\xf2\x09\xb7\xd3\xab\xb6\xd3\xeb\x65\xa7\xd7\xd5\x8e\xf9\xe4\xc9\x54\x2e\x5f\xc3\x0a\x33\xa9\x74\x66\x26\x99\x2f\xa4\x0a\xe7\xf2\x22\x79\x3e\x33\x93\x9f\x3a\x7b\xc6\x66\x2c\x9f\x29\xc8\xe7\x19\xde\x9c\xf9\xc0\x5a\x3d\x7d\xfa\x6c\xde\x51\x95\xc0\xe4\xe9\xa9\x74\xe6\x4c\xed\x69\xbe\x30\x53\x48\x3d\x2e\x1e\xcc\xeb\x4f\xd6\xca\xe3\xc7\x8e\xf5\x27\x3f\x52\x2b\x7a\x92\x87\x65\xd9\x9b\xec\x91\xe5\x11\x94\x3d\x28\xfb\x93\x71\x82\xe3\xa8\x1d\x27\xb4\x27\x0e\x2d\x3c\xee\xc1\x73\x03\x3f\x4a\xd5\x0e\x1f\x45\xb5\xa3\xa8\x76\x14\xd5\x8e\xd6\x47\x24\x99\x39\x9f\x39\x53\x48\x4e\xe5\xce\xf7\x13\x86\x21\xcb\x9f\x4d\x67\x2d\x68\xe1\x5c\xee\x74\x26\x37\xf5\x05\x82\x8e\x1f\x3b\xd6\x97\xec\xa7\xb6\xfb\x61\x0a\xe8\x61\xc0\x86\xdc\x03\x99\xca\x1e\x94\xfd\xc9\x3e\x7a\xdc\x07\xed\x3e\x68\x41\xee\xc1\x63\x2a\x8f\xa0\xec\xa9\x95\x4e\xc7\x7b\x95\x8e\xf7\x3a\x1d\xef\xa5\x46\x7b\xc9\x16\xa4\x23\x28\x0f\x03\xee\xe9\x85\x29\xe3\xf9\x11\x0c\xfd\x11\xb8\x84\xf2\xc8\x61\xe0\x87\x81\xa3\x3c\xd2\x83\xf6\x7a\xf0\x1c\x72\x0f\x64\x2a\xfb\x93\x87\x63\xd4\xb5\xc3\x31\x71\xe7\xbf\xe7\xf1\x5e\x80\xff\x26\xe9\x33\x5c\xf1\x47\xec\x21\xa3\x07\x52\xae\xfd\xc7\x8e\x0f\xf8\xff\x5e\x70\xfe\x18\xdd\x11\xef\x34\xd0\x9f\x63\x38\xa3\x2d\xe2\x26\xb1\x7f\xc7\xaf\x8b\xfe\xbc\x43\xe0\xb5\x9c\x88\xe2\x7d\x93\xa1\x6f\xe0\xff\xec\x62\xff\x75\x94\xeb\x21\x6f\xfb\xff\xe4\x62\x3f\x0e\xfb\xa3\x16\xfb\x01\x85\xfd\x4b\x2e\xf6\xe7\xd0\x68\xa3\xfe\x3f\xef\x62\x7f\x5e\xd1\xff\xa0\xc2\xfe\x39\x17\xfb\x3b\x68\x74\xb4\x41\xff\xa7\x5d\xec\x2f\xc2\xfe\x84\xc5\x7e\xab\xc2\xfe\xaf\x68\x2e\xf3\x0f\x8a\xb4\xd9\xe6\x6d\xff\x61\x4d\x6d\x7f\x17\xf6\x17\x2c\xf6\xf7\x2b\xec\x17\x5c\xec\x47\x3e\x40\x65\xee\x80\xb7\xfd\x9c\x8b\xfd\xf9\x8f\x51\x79\xc1\x62\xff\x80\xc2\xfe\x7d\x2e\xe3\x3f\xdf\x0d\xff\x5b\xbd\xed\x7f\xd0\x65\xfc\x43\x58\xbf\xd6\xf1\x6f\x57\xd8\xff\x8e\x8f\xec\xf3\x1c\x10\xc1\xfb\x65\xce\xc0\xf9\xfa\xfd\xa0\x8b\xfe\x03\x4d\xea\xff\xc0\x45\x7f\xb0\x49\xfd\x7e\x17\xfd\xb1\x26\xf5\x03\x7e\xb5\xfe\x64\x93\xfa\xc7\x5d\xec\x3f\xdd\xa4\x7e\x87\xa6\xd6\x5f\x68\x52\xff\x59\x17\xfd\x95\x5e\x75\x7d\x9e\xbf\xef\x77\xd1\x5f\x75\xd1\xe7\xf2\x57\x70\x0f\xcb\x7f\x6b\xd0\x9f\xdb\x67\xda\xed\xb3\xc4\x9f\x71\x42\xfc\xff\x00\x00\x00\xff\xff\x53\xce\x24\x51\xd8\x47\x00\x00") +var _tcptracerEbpfO = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xc4\x5c\x7d\x6c\x5b\xd7\x75\xbf\x8f\x1f\x16\x25\xd7\x93\xe2\x84\x89\xcc\x26\x9d\xdc\x04\xad\xc6\xa5\x09\xf5\x61\x89\x61\xb2\x56\x73\x9a\x44\xd3\xdc\x4a\x70\xad\x56\x30\x10\xd0\x0c\x2d\x5b\x32\x15\x9b\x16\xe9\x48\x4f\x0c\x50\xb7\x49\x06\x47\xc8\x1f\x72\x92\x61\x5a\xe6\x75\xa2\x2c\x27\xca\xb0\x0d\x1e\xb0\xc2\x42\xd7\x95\x46\x97\x02\x06\x56\x0c\xc2\x90\x0d\xc6\xe0\x0d\xfa\x23\xcd\xb4\xad\x45\xb5\xc5\xcb\xb4\xd6\x08\x07\xde\xf3\x7b\x7c\xef\x9d\x77\xdf\x23\x25\xa7\x2d\x81\xf4\xfa\x9c\x77\xcf\xc7\xbd\xf7\xdc\x73\x7f\xe7\xbe\xa7\x7e\xfd\x89\x03\x4f\xfa\x34\x4d\x18\x3f\x4d\xfc\xaf\x30\x29\xf3\x37\xfd\x84\xf9\xef\x3e\xfc\xef\x5e\xa1\x89\xd2\xdd\xc4\x7b\x49\x08\xf1\x6b\x42\x88\x42\xd3\x46\xb9\x42\xeb\xa9\xac\xe4\x17\x22\x9b\x92\x2e\x2d\x52\xbf\x06\x9f\x10\x1b\xe5\x72\xb9\x74\x11\xb4\x5f\x88\xcd\x72\xb9\xdc\xca\x8c\x5e\x09\x98\x7a\x7d\x15\x1a\xfc\xdf\x47\xab\xdf\x33\xc4\xec\xf6\x4b\x3b\x57\xa0\xa7\x10\xe9\x73\xd8\xed\x57\xd8\x79\x49\x8e\x59\x88\xb0\xd8\x29\x9f\xe8\x3b\x88\x5f\x8f\x9c\x5f\x08\x71\xbc\x41\x88\x36\x21\xc4\x14\xda\xe1\x06\x9f\xc6\xe5\xfb\x3c\xec\x96\x1a\x88\x0e\x37\xfc\x9c\xc6\x75\x1a\xb4\xf6\x7f\x44\x17\x31\x2e\x1f\xc6\x15\x8d\xd1\xfc\xe6\xd0\xcf\xff\x65\xe9\xc7\xb0\xff\x03\xa9\x4f\x9f\x0c\x49\xfe\xd3\xbb\xff\x8b\xe6\xb1\x11\x72\x6f\xac\x97\xa9\x5d\x43\x7b\x1d\xed\x2a\xda\x6b\x68\xaf\xa2\x5d\x41\x7b\x19\xed\x32\xda\x05\xb4\xf3\x55\xbf\x7c\xd2\xaf\x31\xe2\x47\xb2\xe4\xdf\xe9\x16\xac\xc3\x34\xd1\x93\xad\xf0\xbf\x9d\xfa\x45\xcf\x82\xdf\x06\x7e\x1b\xf8\xe7\x88\x9f\x6d\x27\x7e\xd3\x1c\xd1\x67\x62\x44\x1f\x26\xbb\x7a\x3e\x4e\xf3\x33\x4d\xd1\x58\x98\x85\x7f\x03\xe4\x9f\x3e\xdd\x4f\xcf\x27\x87\xa0\x1f\xe3\x99\xbd\x6c\x1b\x67\x6a\x32\x2f\x9f\xa7\xa3\x37\xe0\xcf\x34\x68\x9a\xa7\xd2\x6f\xd0\xfc\x4d\x35\xd2\xfa\x66\xde\x78\x4f\xf2\xa7\x7c\x42\x54\x3c\xca\x44\xdf\x87\x9e\x2c\xe4\x68\x5e\xc7\x27\xcf\x4a\x3a\x13\x5d\xc7\xf3\xa3\x92\x3e\xee\x23\x3d\x29\xfd\x88\xa4\x07\x5f\x25\xfd\x29\x7d\x8c\xda\xdc\x04\xf5\xf3\x53\xbf\xc1\xd7\xb0\x7e\x09\xac\x53\x94\xd6\x69\x54\x4b\xc9\x79\x0f\x6b\xcf\x23\x5e\x5e\xa5\x38\xd6\x34\xc9\x0f\x8a\xbf\xa4\xf5\x47\xdc\x65\x22\x07\xa5\x5c\xf3\xef\x11\x5d\x2a\x22\x3e\x35\x21\x0e\x96\xcb\x65\x63\x1d\x4b\x69\x33\x3e\x2b\x5b\x70\xbc\x48\x72\x99\x08\x8d\x23\x28\xbe\x85\x71\x8e\x94\xa9\xbd\x45\xcf\xa3\x87\xd0\x7e\x08\x3f\xc7\xf0\x7c\x13\xf1\x40\xeb\x6f\xe8\x6f\xbe\xd3\xe9\xc7\x08\xfc\x08\x58\xec\xeb\xa7\xdb\x6a\xca\xdd\x52\xca\xb5\xd7\x94\x3b\x64\x19\xb7\x29\x17\xab\x29\xf7\xa1\x52\x8e\xe2\xb1\xf9\x6e\x67\xff\x31\xf4\x0f\x29\xe6\x57\x3f\x4d\xf1\xab\x5f\xa4\xf9\x52\xd9\xdb\x54\x8c\x2f\xb5\x84\xf9\x1f\xa0\x7c\x90\xba\x48\xeb\x90\x4e\x74\x53\xfc\x2d\x1a\xeb\x11\x27\xba\xf8\x21\xd6\x31\x41\xfd\x2f\xd1\xba\xa4\x0f\x3f\x26\xdb\x70\x20\x63\x8b\xbb\x29\xb4\x61\x3f\xc5\xe5\xd7\x7c\x42\x94\xcb\x42\x84\x7d\xc7\x88\xd6\x40\x6b\x14\xd7\x46\x9e\x2a\x44\xc6\x58\x1e\x9e\xb6\xe5\x05\xeb\xb8\xb2\x18\x57\x0b\xc6\xd5\xc2\xf2\x66\x8c\x9d\x13\x63\x8a\x3c\x1a\x14\x07\x65\x1b\xd6\x3e\x2b\xf3\x60\x58\xfb\x2d\x39\x3f\x95\x7d\x10\x94\xcf\x69\xff\x1b\xfe\xa4\x23\x87\xe0\x0f\xcd\x5f\xf3\x93\xee\x71\xe8\xbd\x5e\x23\xae\xeb\x75\x48\xb1\x5e\xc7\x05\xe6\x15\x6d\x65\x1c\xff\x5d\x2e\x97\x8d\x71\x3c\xdd\xd2\x51\xf5\x53\x93\xfe\x5d\xc5\x7e\x8b\x31\xff\x69\x9d\xf5\x22\xe5\x51\x55\xbc\x59\xf7\x03\xf7\x3f\x55\x44\x9c\x44\x6e\x40\xff\x03\x36\xfd\x99\x08\xc5\x49\xf3\x0e\xa7\xde\x0f\x3d\xf3\x84\x11\x5f\xef\x41\xef\x1e\xe6\x37\xf2\x40\xb1\xcd\xd5\xef\x4d\x4f\xbf\x11\xaf\x91\x35\xe8\xdf\xc5\xfc\x3e\x46\x7a\xbf\xea\xd4\x7b\xcc\xd3\xef\x63\xf0\xfb\x7d\xe8\xa5\x83\x36\x55\x3c\x04\x7b\xd7\x1d\x38\xa2\x9e\xb8\xac\x85\x5f\x7e\x65\xb8\xa9\x95\xe3\xa6\x21\xb6\x5f\xfb\x1d\x76\x87\xbc\xf0\x0b\xe2\x24\xbc\xe3\x96\xa8\x57\xae\xb2\x4f\xf5\x63\xc2\xd1\xdf\x0b\x9f\x99\x38\xe9\xa7\x34\x8e\x2a\x4e\xfa\x09\xd1\x8b\x7c\x1c\x6d\x65\x7b\xbf\x83\x84\x93\xb4\x7f\x23\x9c\x74\x1a\x38\x69\xef\x8f\x84\x5d\x0e\xb8\x28\x82\xf3\x36\x02\x5c\x84\xfd\x58\x88\x00\x47\x44\x80\x23\x22\xc0\x1d\x11\xe0\xa2\xc8\x3c\xda\x39\xb4\x84\x67\xae\xf8\x81\x93\x12\x23\x38\x1f\x8f\x00\x6f\x10\x4e\xd2\x73\xc0\x47\x89\xac\xed\xfc\xd4\x73\x74\xfe\xe9\x79\xe0\xa1\x01\xc2\x4d\x85\x04\x70\x55\x2e\x86\xe7\x71\x3c\x87\xdd\x04\x70\x54\x0e\xf9\x2a\xdf\x8f\xe7\xf0\x33\x01\x1c\x95\x03\x3e\x4a\x2c\xdb\xc6\x95\xca\x03\x17\x0d\xfc\x10\x78\x0b\xb8\x68\x80\xe6\xa5\x84\x7a\x60\x2a\x08\x5c\x74\xf8\x5d\xc2\x45\x01\xe0\xa2\x81\x7f\x82\x1e\xe0\xa2\x01\x9a\xc7\xf1\x3c\x70\xd1\xc0\x75\x3c\x07\xde\x09\x00\x17\x9d\xa1\xf3\x66\xf0\x10\xd6\x63\x00\x38\x2d\x4f\x78\x29\x75\x06\x38\x0a\x76\x07\xe1\x47\xe1\xb0\xb1\x2e\x71\xb4\x74\x2e\x0e\xfb\xde\x16\x95\x10\x2d\xbd\x85\x38\x6b\x10\x62\xa5\x5c\x2e\x37\xf7\x20\xfe\x2c\x79\x22\x66\x39\x8f\x1c\x79\xbf\x08\xbd\xf7\x50\xdc\xe8\xc5\x18\x68\xe8\xb1\xc4\x71\x1b\xdb\xa7\x23\xca\x73\xeb\x75\x1a\x47\x08\xe7\x02\xda\xe1\xd0\x6b\x2c\x1e\x61\x07\xf1\x5c\x88\x6c\xa0\x5d\x47\xde\xfa\x19\xda\x4d\xf0\xd7\x90\xb7\x70\x4e\x9c\xa6\xf5\xe5\x78\xa6\xe2\x57\x8b\xcb\xf8\x43\xb6\xf3\xce\x5d\xbe\x95\xc9\xb7\x6d\x41\xde\x2a\xb7\xb1\x45\xbb\x21\x26\xbf\xae\x94\xaf\x8d\xff\x7e\xb6\x4d\xfc\xb7\xb9\x45\xfc\xb7\x56\x17\x9e\x58\x73\xc5\x13\x4a\x7c\xfb\x36\xe2\xe2\x0d\xca\x27\xfa\x9b\x88\x8f\x13\x94\x57\x4a\x29\x92\x1f\x3c\x4f\xad\xfe\x16\xe2\x66\x06\x79\x65\x19\xf5\xe0\x2c\xe5\x9b\xf1\x25\xc4\xd1\xc0\x34\xf0\xe3\x26\xf0\xe3\x0c\xc3\x97\xcf\x13\x5e\xd4\x3e\x4f\x7e\x7e\x03\x76\xaa\x79\xf6\x51\xe2\xff\x36\xd1\x26\x4e\xa4\xf9\x29\xed\xe5\xfc\x7d\x92\x2e\x0c\x84\x90\x97\x5a\x90\xff\x5a\x59\x3e\xef\xb3\xed\x6f\xbd\x38\x64\xdb\xe7\xd6\xf9\x8a\x2b\x70\xe5\x70\x23\x1d\x54\xc6\xbc\x1c\xd7\xb0\xef\xd0\xb6\xca\x63\xdf\xc4\x63\xf7\xed\xa5\x03\x67\xb8\x61\x8f\x6d\x9c\xb5\xe4\x9e\xde\x4b\x0b\x38\xb5\x83\x9e\xa7\x4f\xd0\xba\x18\xfb\x3b\x3d\xfb\x00\xfc\xa7\xf1\xde\xd0\xc8\xbf\x4c\xa4\x1d\xfc\x56\xc6\x8f\x82\xdf\x82\x7d\xfd\xa0\xe3\x7c\xe6\xf9\x26\xa6\xcc\x37\x18\xbf\x45\x6e\xa4\x0e\xb9\xad\xe0\x98\x90\xc4\x13\x71\xb6\x6e\x94\xe7\xf5\xe3\x94\xd7\xf5\xb1\xac\xc3\x8f\xeb\x5e\xe7\x3e\xee\x2f\xc2\x8d\x94\xc0\x75\xc4\xf3\xb0\x76\x49\x9e\xe7\xa5\xd3\xea\x75\xa9\xe0\xff\x06\xd9\x6f\x41\xea\xe1\x71\x94\xc1\x39\x57\xef\x3d\x4d\x18\xb8\x50\xc7\xf3\xb0\xd6\x24\xed\xdf\xbe\xde\x57\xb0\x3e\x2f\x43\x3f\x9d\xd3\xa5\x17\xa8\x9f\x2a\x1f\x5c\x56\xe4\x9f\xf1\x22\xd9\x1d\xd6\x7e\x52\xf6\xd9\x70\xf7\x65\xc4\xcd\x06\xfc\x5b\x43\x7b\xab\x6c\xf5\x3b\x8d\xf3\x44\x3f\xdf\x5a\xb7\x7d\x5b\x3e\x3a\xdf\x56\x53\x6e\x43\x29\xd7\x5e\x53\x6e\x4d\x95\x6f\xcf\xc7\x6a\xca\xdd\x52\xca\x21\x4f\xb7\x3a\xfb\xf7\x59\xf3\xf4\x0b\xe6\x3a\x91\x9c\x91\xa7\xfb\x5c\xf3\xf4\xba\xb2\x4e\xc7\xfc\x0f\xcc\x23\x8f\x6e\x20\x8f\x5e\x40\x9e\xc5\x7a\x44\x09\x97\x8d\xa3\x3e\xcb\x44\x16\xa9\xff\x25\x5a\x97\xf4\xe1\x25\xca\xbb\x01\x2a\x58\x9d\x75\xfa\xe3\x92\x6f\xd6\xe9\x94\x80\xcd\x3c\xfb\x05\x92\x73\xdc\x57\xd2\x3e\x09\x8a\x2e\xc1\xe3\x76\xde\x33\x6e\x29\x6f\x5f\x01\x2e\x2f\xcc\x00\x1f\xcf\x00\x87\xcd\x5c\x76\xe4\x65\x23\x6e\x24\x6e\xf0\xdb\xe7\xf7\x25\xe9\x43\x25\x7f\xd0\xfc\xea\x17\x29\x8f\xf0\x71\x16\x12\xe6\x3d\xaf\x26\xe3\x96\xce\x81\x54\xf1\xaf\x10\xc7\x87\x40\xcf\x83\x1e\x03\x7d\x01\xf4\x04\x68\x9a\xef\xd2\x6e\xf2\xe3\x06\xce\x8b\x4c\x94\xce\xc3\x29\xe4\x5f\x33\x0f\x4f\x42\x6e\x09\x7a\xf2\xd4\xce\x4c\xa3\x7d\x1e\x79\xda\xa8\x13\x80\xef\x8b\x06\x2e\xa5\x73\xba\xb4\x84\xf9\x08\x50\xbc\x19\xe7\x51\xab\xcf\x3e\xcf\x57\x82\x74\xcf\x5e\x99\x97\x3d\x75\xae\x8b\xdf\x52\xbf\x9a\x79\xca\x47\x79\x0a\xfd\xaa\x75\xe0\x6d\xe7\x99\xff\x29\x37\x29\xcf\xe7\xf9\xed\xe1\xd4\xf3\x43\x4a\x7f\x54\x38\xb5\x4f\x85\xf7\x3c\xe4\x39\x4e\x9d\xdf\x82\x7c\x4d\x9c\xea\x61\xb7\x2e\x9c\x5a\x47\xfe\x53\xe2\xd4\x3a\xf2\x9f\x12\xa7\x7a\xe4\xbf\xb5\xba\xf2\xdf\x16\x71\x6a\xb1\xcf\x56\x57\xea\x97\x10\x1f\xa8\xd7\x06\xc3\x88\x47\xe4\xc5\x42\x82\xf6\x8f\xbe\x04\x7c\x6a\xd4\x8d\x8b\x88\x9f\xe8\x2a\xf2\x24\xf0\x69\xe4\x1f\x68\x5f\x2e\x00\x9f\x36\x51\x1d\x1a\x0e\x16\x49\x7f\x37\xe9\x0f\x07\xfe\x44\xd2\x66\x7e\xfc\x63\xa2\xab\xf9\xf1\x8f\x28\xdf\x38\xf2\xe3\x0e\x99\x1f\xad\xfb\xef\xb2\xe7\xfe\x7b\xd5\x71\xfe\xbb\xf5\xc7\xf9\x4f\xf9\xac\x9a\x47\x51\x87\xcf\xa0\x4e\x9f\x99\x77\xe4\xd1\xf9\x5f\x68\x1e\xbd\x60\xcb\xa3\x3a\xf6\xbb\xbe\x88\x3c\x86\xfc\x68\xdc\xf3\x1a\xf9\x4e\x5f\xc4\x3d\x09\xde\xeb\x54\xdf\xfb\x14\x57\x5d\xf2\x2c\xdd\x4f\x70\xfc\x5c\xba\x8b\xda\x24\x5d\x4b\xca\xfb\x04\x99\x87\xd1\x66\x06\x66\xab\xf9\x59\xe2\xe9\xc8\x2b\xc8\xbf\x73\x68\x5f\x87\xdd\x65\x96\x87\x17\xb6\x9d\x87\x47\x2c\x79\x38\x28\xbe\x47\xf2\xc0\xbd\x1c\xe7\x9a\xf7\x6a\xb1\x2a\x2d\xf7\x53\x08\x71\x3e\xbe\x45\xfc\x5b\xbd\xf7\x7a\x90\xe4\x81\x77\x87\xb5\xa8\x8c\xb7\xc2\x09\xaa\x1b\x4a\x78\xaf\x73\x05\x76\x0a\xb3\xb4\x1e\xe9\x59\xda\x0f\x46\x7d\x5a\xc2\xfb\x24\xd5\xfe\xbd\xa6\xac\xa3\x8d\x7d\x7f\xcd\x75\xdf\xbf\xab\xd8\xf7\x85\x59\x8a\xbb\xcc\xec\x82\x63\x3f\xc4\xb7\x84\xb3\x9b\x09\xe7\x8f\x61\xfe\xde\x6c\x67\xf5\x05\xec\x44\x6e\xd7\xce\x57\xbc\xcf\x49\x8f\x79\x5b\x50\x9e\x93\xf0\x87\xf9\x3d\xac\xad\x33\x7c\x3e\x57\x03\x9f\xc7\xed\xf8\xdc\x78\x8f\xe6\xe1\xcf\x9c\xd7\x7b\x34\x0f\x39\x25\x3e\x37\xee\x51\x3c\xe4\x94\xf8\xdc\xb8\x47\xf1\x90\x53\xe2\x73\xe3\x1e\x65\x8f\xb3\x7f\xdc\x7a\x3e\xbd\x66\xae\xa7\x3d\x4e\xe3\x5b\xc4\xe7\x98\x7f\xdc\x8b\x9a\xf8\x7c\x89\xe1\xf3\x65\x86\xcf\xff\x94\xe1\xf3\x3f\x03\x3e\xa7\x0d\xeb\xc4\xe7\x54\x48\x9b\xe7\x0f\xc5\x9f\x71\xfe\x0c\x6b\xab\xe4\xaf\x25\x7e\x2b\x71\x15\x14\x7f\xfb\xb1\xc4\xe1\xb0\xb6\xc4\xf0\x9a\x71\x4f\x3a\xb7\x3d\xbc\x66\xdc\xcf\x31\x7f\x54\x78\x2d\xee\x75\xbf\xa7\x90\xe7\x78\x6d\x6e\x0b\xf2\x75\xdf\x2b\x2a\xec\x6e\xe9\x5e\xd1\xc3\xae\xe7\xbd\xa2\x87\x9c\xe7\xbd\xa2\x62\x3f\xac\xd5\xb5\x1f\xb6\x88\xd7\x96\x10\x17\xc6\x77\x14\x17\x8d\xf7\x09\xcb\x38\xe7\x11\x27\x51\xe0\x82\x22\x70\x1a\xde\x8b\x8c\x2f\x20\x6e\x9a\x08\x17\x8c\xe3\x3d\x73\xe6\xf0\xf7\x69\xbf\xbc\x05\x9c\x36\xf3\x0e\xe5\xc7\x5e\xf2\x67\xf0\x29\x6a\xc3\x0d\xe3\xe4\x27\xee\xd5\x07\x3f\x6b\xf0\xf1\xde\x19\x68\x21\x2c\xe8\x3d\xc0\xd7\x82\xa0\x83\xcf\x48\x7a\x38\x10\x94\x2d\xdf\x7f\xad\x72\xfb\x59\xee\xf7\x1e\xa7\xfb\xb0\x61\xdf\x2c\xf5\xaf\x79\xaf\xf7\x52\x75\x7e\x83\x96\x7b\x2e\x7e\x7f\x5f\x98\xed\xab\x9e\x4f\x7e\xb9\x5f\xfa\x31\x4f\x31\x86\x5b\x0e\xb0\x73\x0c\xef\x85\x0c\x1c\xb6\x48\xf9\x24\x8d\xf7\x44\xa9\xc5\xef\x82\x3e\x8a\x75\xa0\xf9\x2d\xdd\x4f\xf2\x37\xb0\xef\x32\x09\xc2\x65\xad\x0d\x76\xff\x8d\x7e\x49\x7c\x37\x34\x85\xfe\xa6\xdc\x09\xf2\x0f\xdf\xa7\xa4\xa3\x13\xd8\xef\x59\xb4\x79\x07\x0e\x1d\x52\xdc\xb3\xea\x45\xd4\xdb\x8b\xc0\x87\x17\x71\x1f\x0d\x1c\x3e\x85\xf7\x3c\x25\xda\x0e\x8e\xf7\xe2\x25\xd4\x03\x61\x81\x73\xce\xa0\x03\x04\xb8\x4a\xf4\xba\xac\x1a\x07\x66\xff\xdd\xb6\xe7\xc9\xac\xb0\xcb\x4b\x54\x66\xc7\x7d\x71\x0f\xdc\x67\xd4\x3f\xbc\x0e\x37\xc6\xe7\x3a\xae\xdd\x76\xbb\xe9\x84\x71\xbe\x10\x2e\xbd\x11\xc4\x7c\xe3\x3b\xa6\xe4\x24\xe4\x31\x8f\x26\x3e\x7e\x83\xc5\xcb\x05\x07\xbe\x99\xf3\xac\x43\xde\xff\x88\xf7\x5f\xf0\xec\xbf\xf6\x91\xf8\xa5\xc6\xf7\x02\x8b\xef\x25\x5b\x7c\xa7\x16\x97\x3d\xe3\xdb\x39\x5f\x27\xd8\xbe\xd9\x5e\xfc\xde\x6e\x7c\x04\xc5\x95\x5f\xea\x3c\xea\x8b\x46\x9e\x46\xbd\x83\xf9\xa9\xbe\x5f\x5e\x44\x9e\xbe\x88\xfa\x0d\xef\x8d\x8d\x3a\xce\x3d\x8f\x50\x1d\xd7\x2a\xeb\x4b\x4b\x1e\x41\x5d\x96\xb4\xc4\xbd\xbd\x3e\xfb\x26\xcb\x23\x2f\x62\xfe\xcf\xa1\x7d\xe5\x17\xb6\x0e\x23\x72\xce\xff\x9e\xe6\xab\xfa\xbd\xa4\x81\x5f\x30\xbf\x7b\xec\xdf\x3f\x18\xdf\x2f\xf0\x3a\x2d\xdc\x48\xf7\xa7\xd6\xfd\xa3\xfa\xde\xc2\x59\xa7\x25\x64\x6b\xd6\x69\x71\xaa\xd3\x8c\xf5\x86\xfe\xcc\x2c\xbe\x97\x99\xbd\x09\x3c\x55\x1b\x1f\xdc\xdc\x02\xae\x68\xc0\xf7\x06\xb5\xbe\x5b\x2b\xcc\x02\xbf\xce\x6e\xd4\x5d\x27\xaa\x71\x91\x81\x37\xd6\x5d\xf1\x86\xaa\xbe\x28\xcc\xce\x61\x1e\x86\xea\xce\x6f\xea\xfa\x6d\x17\x7b\x1f\x03\xbd\x91\xdb\xd5\xfb\xa5\x6d\xd7\x85\x32\xae\x43\x78\x6f\xe6\xc0\xe5\x43\xc0\xe5\xff\x5e\xf6\xd9\xf2\x05\xbe\x77\xc5\xbd\x19\xc7\x25\xfc\x9e\x35\x8d\xfa\xda\xac\x27\xbd\xbf\x53\x49\x01\xbf\xa5\xa3\xef\x60\x3f\xe2\x9e\x20\x42\xdf\x79\x98\xef\x2d\x97\x10\x0f\xb5\xeb\xcd\x15\x8f\x7a\x53\x85\x5b\xbf\x6b\xfd\xce\x8b\xe1\x56\xf3\x3b\x32\xe0\xc7\x22\xed\x0f\xf3\x1e\xfe\xfb\x8e\xfc\xb1\xac\xc4\x21\x34\x7e\xe3\x3c\x36\xbe\xbb\x35\xf2\x52\xd8\xf7\x03\xca\x6f\x96\x7c\x52\x99\xc2\xcf\xed\x06\x4e\xc5\xbd\x96\x21\x6f\xd6\x6d\xdf\x53\xae\x0b\xe5\x1b\x4d\x7c\x6e\xf7\x77\x48\xce\x92\xbf\x2a\xf1\x66\x7c\x1f\xc9\xf3\x97\xf1\x3e\x29\x28\x66\x3f\xe6\xb8\x7a\x5b\xd6\x7b\x1f\x5f\x5c\x6d\xef\xbb\x28\x33\xde\x6e\x20\xce\x8c\x3a\xf2\x3d\x97\x78\xab\x5d\xd7\xa9\xef\xa9\xea\xaf\xeb\x56\xb7\x28\xcf\xeb\xd9\x15\x0f\x79\x23\xde\x1b\x1a\x9d\x75\xec\x55\x8f\x7a\xcd\x8c\xfb\xeb\x2e\x71\xff\xcf\x75\xc6\xbd\x71\x1f\x4b\xeb\x31\xec\xc3\x7b\x7f\x8c\xa7\x76\x5d\xd4\x88\xb8\xa4\x75\xd6\x97\x68\x9d\x87\x03\x8f\x90\x9e\x7d\xd0\x83\xef\xaf\x8c\xef\xbf\x28\x4b\x58\xea\xa5\x60\x57\x75\x7c\x14\x7f\xc0\x2b\x6f\x21\xce\x66\x46\xaa\xf1\xe5\x93\xf1\x40\x78\x25\x7d\xe2\x28\xc3\x3b\xf6\xfb\xe6\xd4\x22\xf2\x56\x74\x1a\xf4\xbb\xa0\x9f\x07\x4d\xf3\x97\xc1\x3d\xf4\x54\x15\x1f\x7e\x93\xc5\x63\x6d\x5c\x32\xa6\x9c\xdf\xe9\x1a\x79\x25\xe1\x92\x57\x7a\x91\x57\xce\xba\xe4\x95\xae\x1a\x79\xe5\x61\x47\x5e\x19\xf2\xc8\x2b\x25\xd4\x79\x4e\x7c\xfa\x19\xd9\x7f\x10\xdf\xe1\x84\x35\xfa\x1e\x65\xf0\x71\xd0\xfe\x4f\xc3\xcf\xeb\x2e\x7e\x7e\xaa\x86\x9f\x91\x6d\xe5\x3f\x13\xbf\xfd\x07\xc9\x3b\xbe\x8f\x25\x1c\x67\xd4\x89\xe9\x26\xe0\xa6\xea\xf7\xb2\xeb\x8e\x73\xfe\xa6\xe7\x39\xdf\x24\xe9\x54\xf5\x9c\x27\x60\xc6\xf1\x9e\xf5\xfb\x98\xf5\x8f\xe5\x3b\x5f\xe7\x77\xbd\xf5\xe1\xca\x07\x94\xfe\xb9\xd5\x73\x1c\xcf\x9a\xfb\x70\xc5\xa5\x1e\xb9\x5a\xdd\x77\x01\x0b\x2e\x30\xf7\xa1\xfd\x7e\xd0\xb8\x0f\xe4\xdf\xcb\x9a\xdf\x97\x13\x9e\x48\x01\x07\x1b\xf7\xdb\xaa\x3c\xec\x86\xfb\x57\xb6\x51\x7f\xf1\xf9\x0f\x58\xfe\x13\x01\x77\x9e\x86\xf9\x0a\xd5\xc1\x6b\x31\xf2\x9d\x85\xd7\xae\xe0\x85\x14\x36\xdc\xec\x9e\xc3\xbf\x2b\xbf\xa7\x86\x0e\x88\x8f\x2c\x7f\x1b\xa0\xfa\xdd\x2f\xe5\x83\xa2\x65\x87\x9d\xff\x37\x7e\xe2\x6f\x30\xfe\x41\xf0\x97\x1b\xec\xfc\xbb\xc0\x6f\xf7\xdb\xf9\x7f\xe8\x23\xfe\x10\xeb\x3f\x09\x7e\x28\x64\xe7\x3f\x06\x7e\x8c\xd9\xbd\x13\x7e\x1e\x61\xfd\x3f\x80\xdd\x2c\xe3\xbf\x0b\xfe\x59\xe6\xcf\x41\xe8\x5f\x60\xfd\x1f\x00\xff\x32\xe3\x0f\x48\xf9\x46\x11\x63\xfe\x9f\xf7\x11\x7f\x8c\xf1\x4f\x80\x2f\x98\x9e\x6e\xf0\x63\x8c\xdf\x15\x20\x7e\x1f\xe3\xff\x2e\xfa\xcf\x31\xff\xbf\x2d\xe9\x9d\x62\x95\xf1\x3f\x01\xfe\x58\xd0\xce\x7f\xd9\x47\xfc\x79\xc6\x7f\x06\xfc\x65\xc6\x7f\x10\xfc\x15\xc6\xef\x84\xfe\x75\xe6\xe7\x2d\xf4\x6f\x6d\xb4\xf3\xbf\x85\xfe\xcb\x76\xb6\xf8\x11\xfa\xf3\x3f\x0e\x9d\x01\x7f\x88\xf1\x3f\x29\xe7\x67\x97\x18\x09\xd8\xf9\x3f\xf7\x13\xff\x5c\x93\x9d\x7f\x03\xfc\xeb\x8c\xbf\xdf\xe0\x33\xfd\x01\xf0\xb3\x4c\xff\x4e\xd8\x5d\x66\x7a\xfe\x1c\xfd\xaf\xb1\x75\xbf\xe9\x23\xfe\x26\xe3\xfb\xa1\xa7\x9d\xe9\xf9\x4f\xe8\x69\xfb\x84\x9d\xff\x43\xf0\x5b\x18\xff\x92\x8b\xff\x7f\x60\xf8\xcf\xf8\xf7\x83\x7f\x96\xf1\xff\x15\x7e\xae\x33\x7e\x42\xf6\x6f\x16\x2b\x6c\x1e\x0e\xfb\x88\xdf\xc7\xc6\xf5\x9b\x01\xe2\x9f\x63\xfc\x28\xf4\x84\x58\x3c\xfc\x18\x7a\x5a\x18\xff\x1b\xe0\xb7\x31\xfe\xbd\xd0\xb3\xc9\xfc\xfc\x22\xfa\x0f\xb1\x3c\x7e\x1f\xf8\x47\x18\xff\x07\x52\xcf\x1d\x62\xc5\xce\x16\xef\x49\xfe\x9d\x8e\x79\x0b\xc8\x73\xe4\x53\x82\xff\xbe\x24\xf1\xf3\xaf\x3b\xf8\x39\x89\xeb\xef\x71\xf0\xbf\x23\xf5\xb4\x3a\xf8\x9f\x97\x7a\xee\x75\xf0\x9f\x90\xe7\xc1\x27\x1d\xfc\x09\xd9\xff\x0e\x07\xff\x5f\x24\x7f\x97\x83\xff\x17\x92\xbf\xd3\xc1\xff\x47\xa9\xdf\xef\xe0\x3f\x2b\xf9\x0d\x0e\xfe\x57\xa5\x9e\x3b\x1d\xfc\x06\xd9\xbf\xd9\xc1\xff\x6b\xc9\x0f\x3a\xf8\x5f\x91\xfc\x46\x07\x3f\x29\xe7\x2d\xec\xe0\xbf\x29\xe7\xed\x6e\x07\xbf\x5d\xfa\x73\x9f\x83\xff\xb2\xd4\xb3\xc7\xc1\x5f\x95\x7a\x22\x0e\x7e\x3f\xda\xca\xb2\x57\x2a\x91\x38\xa3\xb3\x8c\xbe\x6c\xa1\x2b\xb2\x9b\x41\x93\xfe\x1d\x21\xc4\x5c\xc8\xfe\xdc\xaa\x3f\xc1\xf4\x27\x98\xfe\x0a\xbd\xc0\xf4\x4f\xfb\xed\xf4\x7a\x83\xdd\x5e\x9b\xc5\xde\x00\xb3\x57\xf9\xf7\x2a\xa3\xdb\x35\x3b\xbd\xe6\xb7\xeb\xeb\xb7\x8c\xe7\xc9\x8a\x7d\x36\xbe\x4d\x66\xaf\xbd\xd1\x4e\xaf\x36\x99\xf4\x53\x42\xe0\xaf\x24\x4d\x7b\x7d\xcc\xfe\x32\xa3\x57\x1b\xed\xf6\xcf\x35\xd9\xed\xaf\x34\xd9\xed\x6d\xec\x64\xfe\xee\xb2\xdb\x1f\x61\xf6\xaf\x31\x7b\x6d\x3e\x3b\x3d\x1f\xb0\xeb\xdb\x08\xd9\xe9\xf9\x46\xbb\x7e\xeb\x7c\x3f\x5a\x79\x6e\xa1\x1f\x03\x7e\xb3\xd2\x47\x18\xdd\xaf\xd9\xf5\x8b\x87\xf2\xa3\xd3\x79\x91\x99\x1c\xcd\x67\x27\x4f\x3d\x33\x9a\x4c\x8e\x9f\x1c\xcd\x27\xd3\xb9\x4c\x32\x95\x4e\x8f\x66\xf3\xe2\xa1\xc9\xd1\x89\xea\xe3\x87\xf9\xd3\x67\x53\xd9\xdc\xc3\xc7\x8e\x8e\x9f\xcc\xe5\x53\x13\x13\xc9\xc9\x51\x9b\xae\x7c\x3a\x9b\x7c\xae\x27\x99\x3e\x75\xf2\xe4\x68\x3a\x2f\x32\x6a\xb6\xdd\x82\xea\xa1\xf2\x09\xb7\xd3\xad\xb6\xd3\xed\x65\xa7\xdb\xd5\x8e\xf9\x44\x0e\x31\x9f\xce\xe6\x27\x53\xe9\xd1\xc9\x64\x2e\x9f\xca\x9f\xc9\xf1\x81\x67\xc7\x8f\xe6\x44\xf2\xb9\xd1\xc9\xdc\xf8\xa9\x93\x56\xd7\x8e\x1d\x4d\xa2\x4f\xd5\x2d\x0b\xcb\xee\x12\x7f\xe0\xe0\x5a\x07\x96\x1b\xcd\x4b\x5f\x46\xb9\xeb\xe6\x03\x6b\xf7\xf4\xc4\xa9\x9c\xa3\x2b\x31\x93\x13\xe3\xe9\xd1\x93\x95\xa7\xb9\xfc\x64\x3e\xf5\x8c\x78\x28\xa7\x3f\x5b\x69\x0f\xec\xdf\xdf\x93\x7c\xa4\xd2\x74\x24\x3b\x65\xdb\x9d\xec\x90\x6d\x17\xda\x0e\xb4\x3d\xc9\x38\xb1\xe3\xe8\x1d\x27\x6e\x47\x1c\x52\x78\xdc\x81\xe7\x06\xbf\x97\xba\x75\xf6\xa2\x5b\x2f\xba\xf5\xa2\x5b\x6f\x75\xf6\x93\xa3\xcf\x8d\x9e\xcc\x27\xc7\xb3\xcf\xf5\x10\x0f\xcb\x93\x3b\x95\xce\x58\xb8\xf9\x33\xd9\x89\xd1\xec\xf8\x51\x62\x1d\xd8\xbf\x7f\x5f\xb2\x87\x74\xf7\xc0\x14\xb8\x9d\x60\x1b\x74\x07\x68\x6a\x3b\xd0\xf6\x24\xf7\xd1\xe3\x7d\x90\xde\x07\x29\xd0\x1d\x78\x4c\x6d\x17\xda\x8e\x4a\xeb\x74\xbc\x5b\xe9\x78\xb7\xd3\xf1\x6e\x52\xda\x4d\xb6\x40\x75\xa1\xed\x04\xbb\xa3\x1b\xa6\x64\xdb\x9b\xec\x42\xb7\x2e\xac\x00\xe8\x0e\xd9\xc6\x93\x9d\x78\xdc\x89\xc7\xa0\x3b\x0c\x7e\x07\xb4\x77\xe0\x39\xe8\x0e\xd0\xd4\xf6\x24\x3b\x63\x34\xd0\x4e\xbc\xf7\xbe\xdd\xdf\x2e\x9f\x50\xa0\x02\x21\xe2\xf8\xbb\xab\x47\x19\x1e\x64\x70\x49\xd2\x95\xff\x58\xb9\x86\xff\x6f\x17\xe7\x2f\xc0\xed\x6b\xde\xf2\xd3\x8c\xcf\xca\x0f\xd1\xa8\x09\x05\xba\x10\x62\x99\xfe\xac\x4c\xe0\x35\xbe\xbc\x9b\xf0\x5b\xe4\x0d\xfe\x86\x8b\xff\x06\x5e\xbc\x1a\xf2\xb6\xff\x63\xa1\xb6\xbf\x02\xfb\x43\x16\xfb\x41\x85\xfd\x4b\x2e\xf6\xa7\xa1\xb4\xd6\xf8\x8b\x2e\xf6\x43\x5f\xb0\xdb\x69\x03\x76\xe3\xf6\xcf\xb9\xd8\xdf\x80\xd2\xa1\x1a\xe3\x7f\xd1\xc5\x7e\x2b\xec\x1f\xb1\xd8\x6f\x54\xd8\xbf\xe6\xb6\xfe\xb8\x2f\xdc\x6c\xf2\xb6\xff\x8e\xcb\xfa\x4f\xc3\xfe\x9c\xc5\xfe\x4e\x85\xfd\x0f\x5c\xec\xc7\x01\x35\x17\x76\x79\xdb\xff\xa9\x8b\xfd\x10\x02\xf8\xac\xc5\xfe\x2e\x85\xfd\xcf\xb8\xcc\xff\xf5\x28\xb5\x57\x1b\xbd\xed\x7f\xda\x65\xfe\xcf\xc1\xbe\x75\xfe\x9b\x15\xf6\x2f\xb8\x8c\x7f\x1e\xef\xe5\xaf\xf1\x71\xf1\x7e\x2e\xe3\x5f\xef\x33\xed\x1a\xed\x1d\x0a\xfb\x2f\xb8\xd8\x3f\x82\xf7\x03\xbc\x7e\xe5\xf6\xbf\xee\x36\xff\xc8\x5f\x31\x8b\xfd\x3b\x15\xf6\x0f\xfb\xc9\x3e\xcf\x81\x6b\xf8\xbb\xee\xbb\x18\x9f\xe7\xaf\xa2\x4f\x2d\x7f\xb3\x4e\xf9\x93\x2e\xf6\x43\xbd\xf5\xc9\x7f\xdb\xc5\xfe\xbd\x75\xca\xbf\xe8\x62\x3f\x56\xa7\xfc\xdf\xb9\xd8\xff\x62\x9d\xf2\x7d\x42\x2d\x3f\x52\xa7\xfc\x23\x9a\x5a\x7e\xa2\x4e\xf9\xfb\x5d\xe4\xcf\xd6\x29\x1f\x74\x19\xff\xeb\xbd\xea\xfe\xfc\xfc\xfc\xb2\x8b\xfd\x79\x17\x79\x4e\x87\xf1\xde\x96\xff\x16\x20\x1f\xb2\xd4\x8f\xbd\x96\xf8\x37\x6e\x26\xfe\x3f\x00\x00\xff\xff\x30\x48\xc9\x24\xb8\x4d\x00\x00") func tcptracerEbpfOBytes() ([]byte, error) { return bindataRead( @@ -83,7 +83,7 @@ func tcptracerEbpfO() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "tcptracer-ebpf.o", size: 18392, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "tcptracer-ebpf.o", size: 19896, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer.go b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer.go index b84ae0d1e2..39f08974b3 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer.go @@ -5,6 +5,7 @@ package tracer import ( "bytes" "fmt" + "unsafe" bpflib "github.com/iovisor/gobpf/elf" ) @@ -111,6 +112,19 @@ func NewTracer(tcpEventCbV4 func(TcpV4), tcpEventCbV6 func(TcpV6), lostCb func(l }, nil } +func (t *Tracer) AddFdInstallWatcher(pid uint32) (err error) { + var one uint32 = 1 + mapFdInstall := t.m.Map("fdinstall_pids") + err = t.m.UpdateElement(mapFdInstall, unsafe.Pointer(&pid), unsafe.Pointer(&one), 0) + return err +} + +func (t *Tracer) RemoveFdInstallWatcher(pid uint32) (err error) { + mapFdInstall := t.m.Map("fdinstall_pids") + err = t.m.DeleteElement(mapFdInstall, unsafe.Pointer(&pid)) + return err +} + func (t *Tracer) Stop() { close(t.stopChan) t.perfMapIPV4.PollStop() diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer_unsupported.go b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer_unsupported.go index 848dc2a22e..8eb1a02f84 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer_unsupported.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/pkg/tracer/tracer_unsupported.go @@ -15,6 +15,12 @@ func TracerAsset() ([]byte, error) { func NewTracer(tcpEventCbV4 func(TcpV4), tcpEventCbV6 func(TcpV6), lostCb func(lost uint64)) (*Tracer, error) { return nil, fmt.Errorf("not supported on non-Linux systems") } +func (t *Tracer) AddFdInstallWatcher(pid uint32) (err error) { + return fmt.Errorf("not supported on non-Linux systems") +} +func (t *Tracer) RemoveFdInstallWatcher(pid uint32) (err error) { + return fmt.Errorf("not supported on non-Linux systems") +} func (t *Tracer) Stop() { } diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.c b/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.c index 1886f31b61..b6d16ae69c 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.c +++ b/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.c @@ -78,6 +78,26 @@ struct bpf_map_def SEC("maps/tuplepid_ipv6") tuplepid_ipv6 = { .max_entries = 1024, }; +/* This is a key/value store with the keys being a pid + * and the values being a fd unsigned int. + */ +struct bpf_map_def SEC("maps/fdinstall_ret") fdinstall_ret = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(__u64), + .value_size = sizeof(unsigned int), + .max_entries = 1024, +}; + +/* This is a key/value store with the keys being a pid (tgid) + * and the values being a boolean. + */ +struct bpf_map_def SEC("maps/fdinstall_pids") fdinstall_pids = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = 1024, +}; + /* http://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-a-c-program */ __attribute__((always_inline)) static bool is_big_endian(void) @@ -821,6 +841,48 @@ int kretprobe__inet_csk_accept(struct pt_regs *ctx) return 0; } +SEC("kprobe/fd_install") +int kprobe__fd_install(struct pt_regs *ctx) +{ + u64 pid = bpf_get_current_pid_tgid(); + u32 tgid = pid >> 32; + unsigned long fd = (unsigned long) PT_REGS_PARM1(ctx); + u32 *exists = NULL; + + exists = bpf_map_lookup_elem(&fdinstall_pids, &tgid); + if (exists == NULL || !*exists) + return 0; + + bpf_map_update_elem(&fdinstall_ret, &pid, &fd, BPF_ANY); + + return 0; +} + +SEC("kretprobe/fd_install") +int kretprobe__fd_install(struct pt_regs *ctx) +{ + u64 pid = bpf_get_current_pid_tgid(); + unsigned long *fd; + fd = bpf_map_lookup_elem(&fdinstall_ret, &pid); + if (fd == NULL) { + return 0; // missed entry + } + bpf_map_delete_elem(&fdinstall_ret, &pid); + + u32 cpu = bpf_get_smp_processor_id(); + struct tcp_ipv4_event_t evt = { + .timestamp = bpf_ktime_get_ns(), + .cpu = cpu, + .type = TCP_EVENT_TYPE_FD_INSTALL, + }; + evt.pid = pid >> 32; + evt.fd = *(__u32*)fd; + bpf_get_current_comm(&evt.comm, sizeof(evt.comm)); + bpf_perf_event_output(ctx, &tcp_event_ipv4, cpu, &evt, sizeof(evt)); + + return 0; +} + char _license[] SEC("license") = "GPL"; // this number will be interpreted by gobpf-elf-loader to set the current // running kernel version diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.h b/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.h index 8cdbfb78d9..49a9d5a251 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.h +++ b/vendor/github.com/weaveworks/tcptracer-bpf/tcptracer-bpf.h @@ -3,9 +3,10 @@ #include -#define TCP_EVENT_TYPE_CONNECT 1 -#define TCP_EVENT_TYPE_ACCEPT 2 -#define TCP_EVENT_TYPE_CLOSE 3 +#define TCP_EVENT_TYPE_CONNECT 1 +#define TCP_EVENT_TYPE_ACCEPT 2 +#define TCP_EVENT_TYPE_CLOSE 3 +#define TCP_EVENT_TYPE_FD_INSTALL 4 #define GUESS_SADDR 0 #define GUESS_DADDR 1 @@ -30,6 +31,8 @@ struct tcp_ipv4_event_t { __u16 sport; __u16 dport; __u32 netns; + __u32 fd; + __u32 dummy; }; struct tcp_ipv6_event_t { @@ -46,6 +49,8 @@ struct tcp_ipv6_event_t { __u16 sport; __u16 dport; __u32 netns; + __u32 fd; + __u32 dummy; }; // tcp_set_state doesn't run in the context of the process that initiated the diff --git a/vendor/github.com/weaveworks/tcptracer-bpf/tests/tracer.go b/vendor/github.com/weaveworks/tcptracer-bpf/tests/tracer.go index ba491e9991..fcbea950b2 100644 --- a/vendor/github.com/weaveworks/tcptracer-bpf/tests/tracer.go +++ b/vendor/github.com/weaveworks/tcptracer-bpf/tests/tracer.go @@ -1,19 +1,28 @@ package main import ( + "flag" "fmt" "os" "os/signal" + "strconv" + "strings" "github.com/weaveworks/tcptracer-bpf/pkg/tracer" ) +var watchFdInstallPids string var lastTimestampV4 uint64 var lastTimestampV6 uint64 func tcpEventCbV4(e tracer.TcpV4) { - fmt.Printf("%v cpu#%d %s %v %s %v:%v %v:%v %v\n", - e.Timestamp, e.CPU, e.Type, e.Pid, e.Comm, e.SAddr, e.SPort, e.DAddr, e.DPort, e.NetNS) + if e.Type == tracer.EventFdInstall { + fmt.Printf("%v cpu#%d %s %v %s %v\n", + e.Timestamp, e.CPU, e.Type, e.Pid, e.Comm, e.Fd) + } else { + fmt.Printf("%v cpu#%d %s %v %s %v:%v %v:%v %v\n", + e.Timestamp, e.CPU, e.Type, e.Pid, e.Comm, e.SAddr, e.SPort, e.DAddr, e.DPort, e.NetNS) + } if lastTimestampV4 > e.Timestamp { fmt.Printf("ERROR: late event!\n") @@ -40,9 +49,15 @@ func lostCb(count uint64) { os.Exit(1) } +func init() { + flag.StringVar(&watchFdInstallPids, "monitor-fdinstall-pids", "", "a comma-separated list of pids that need to be monitored for fdinstall events") + + flag.Parse() +} + func main() { - if len(os.Args) != 1 { - fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0]) + if flag.NArg() > 1 { + flag.Usage() os.Exit(1) } @@ -52,6 +67,20 @@ func main() { os.Exit(1) } + for _, p := range strings.Split(watchFdInstallPids, ",") { + if p == "" { + continue + } + + pid, err := strconv.ParseUint(p, 10, 32) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid pid: %v\n", err) + os.Exit(1) + } + fmt.Printf("Monitor fdinstall events for pid %d\n", pid) + t.AddFdInstallWatcher(uint32(pid)) + } + fmt.Printf("Ready\n") sig := make(chan os.Signal, 1) diff --git a/vendor/manifest b/vendor/manifest index 9c21dabe74..e06b5a4e64 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -1462,7 +1462,7 @@ "importpath": "github.com/weaveworks/tcptracer-bpf", "repository": "https://github.com/weaveworks/tcptracer-bpf", "vcs": "git", - "revision": "a82fffdbfee2ffe2c469279dbfeb3734cf7de1f2", + "revision": "783f088bbe3e91d4d23cf2f48072f80de2fd03fc", "branch": "master", "notests": true }, From d715ccc391aad0668912c9138aa076803af720db Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Thu, 11 May 2017 15:18:06 +0200 Subject: [PATCH 2/4] ebpf: handle fd_install events from tcptracer-bpf Since https://github.com/weaveworks/tcptracer-bpf/pull/39, tcptracer-bpf can generate "fd_install" events when a process installs a new file descriptor in its fd table. Those events must be requested explicitely on a per-pid basis with tracer.AddFdInstallWatcher(pid). This is useful to know about "accept" events that would otherwise be missed because kretprobes are not triggered for functions that were called before the installation of the kretprobe. This patch find all the processes that are currently blocked on an accept() syscall during the EbpfTracker initialization. feedInitialConnections() will use tracer.AddFdInstallWatcher() to subscribe to fd_install events. When a fd_install event is received, synthesise an accept event with the connection tuple and the network namespace (from /proc). --- probe/endpoint/connection_tracker.go | 12 ++- probe/endpoint/ebpf.go | 89 ++++++++++++++++++- probe/endpoint/procspy/proc_darwin.go | 16 ++++ probe/endpoint/procspy/proc_internal_test.go | 2 +- probe/endpoint/procspy/proc_linux.go | 74 +++++++++------ probe/endpoint/procspy/procnet.go | 6 +- .../endpoint/procspy/procnet_internal_test.go | 14 +-- probe/endpoint/procspy/spy.go | 2 +- probe/endpoint/procspy/spy_linux.go | 2 +- .../procspy/spy_linux_internal_test.go | 4 +- probe/process/walker.go | 19 ++-- probe/process/walker_darwin.go | 9 +- probe/process/walker_linux.go | 60 ++++++++++--- probe/process/walker_linux_test.go | 2 +- probe/process/walker_test.go | 2 +- prog/probe.go | 2 +- 16 files changed, 239 insertions(+), 76 deletions(-) create mode 100644 probe/endpoint/procspy/proc_darwin.go diff --git a/probe/endpoint/connection_tracker.go b/probe/endpoint/connection_tracker.go index b1ed086626..fcd60179c8 100644 --- a/probe/endpoint/connection_tracker.go +++ b/probe/endpoint/connection_tracker.go @@ -171,7 +171,8 @@ func (t *connectionTracker) performWalkProc(rpt *report.Report, hostNodeID strin // once to initialize ebpfTracker func (t *connectionTracker) getInitialState() { var processCache *process.CachingWalker - processCache = process.NewCachingWalker(process.NewWalker(t.conf.ProcRoot)) + walker := process.NewWalker(t.conf.ProcRoot, true) + processCache = process.NewCachingWalker(walker) processCache.Tick() scanner := procspy.NewSyncConnectionScanner(processCache) @@ -194,7 +195,14 @@ func (t *connectionTracker) getInitialState() { } scanner.Stop() - t.ebpfTracker.feedInitialConnections(conns, seenTuples, report.MakeHostNodeID(t.conf.HostID)) + processesWaitingInAccept := []int{} + processCache.Walk(func(p, prev process.Process) { + if p.IsWaitingInAccept { + processesWaitingInAccept = append(processesWaitingInAccept, p.PID) + } + }) + + t.ebpfTracker.feedInitialConnections(conns, seenTuples, processesWaitingInAccept, report.MakeHostNodeID(t.conf.HostID)) } func (t *connectionTracker) performEbpfTrack(rpt *report.Report, hostNodeID string) error { diff --git a/probe/endpoint/ebpf.go b/probe/endpoint/ebpf.go index 83726132ae..1cce0c5641 100644 --- a/probe/endpoint/ebpf.go +++ b/probe/endpoint/ebpf.go @@ -1,14 +1,18 @@ package endpoint import ( + "bytes" "fmt" "regexp" "strconv" "sync" + "syscall" log "github.com/Sirupsen/logrus" + "github.com/weaveworks/common/fs" "github.com/weaveworks/scope/probe/endpoint/procspy" "github.com/weaveworks/scope/probe/host" + "github.com/weaveworks/scope/probe/process" "github.com/weaveworks/tcptracer-bpf/pkg/tracer" ) @@ -23,7 +27,7 @@ type ebpfConnection struct { type eventTracker interface { handleConnection(ev tracer.EventType, tuple fourTuple, pid int, networkNamespace string) walkConnections(f func(ebpfConnection)) - feedInitialConnections(ci procspy.ConnIter, seenTuples map[string]fourTuple, hostNodeID string) + feedInitialConnections(ci procspy.ConnIter, seenTuples map[string]fourTuple, processesWaitingInAccept []int, hostNodeID string) isReadyToHandleConnections() bool isDead() bool stop() @@ -111,8 +115,12 @@ func tcpEventCbV4(e tracer.TcpV4) { lastTimestampV4 = e.Timestamp - tuple := fourTuple{e.SAddr.String(), e.DAddr.String(), e.SPort, e.DPort} - ebpfTracker.handleConnection(e.Type, tuple, int(e.Pid), strconv.Itoa(int(e.NetNS))) + if e.Type == tracer.EventFdInstall { + ebpfTracker.handleFdInstall(e.Type, int(e.Pid), int(e.Fd)) + } else { + tuple := fourTuple{e.SAddr.String(), e.DAddr.String(), e.SPort, e.DPort} + ebpfTracker.handleConnection(e.Type, tuple, int(e.Pid), strconv.Itoa(int(e.NetNS))) + } } func tcpEventCbV6(e tracer.TcpV6) { @@ -125,6 +133,73 @@ func lostCb(count uint64) { ebpfTracker.stop() } +func tupleFromPidFd(pid int, fd int) (tuple fourTuple, netns string, ok bool) { + // read /proc/$pid/ns/net + // + // probe/endpoint/procspy/proc_linux.go supports Linux < 3.8 but we + // don't need that here since ebpf-enabled kernels will be > 3.8 + netnsIno, err := procspy.ReadNetnsFromPID(pid) + if err != nil { + log.Debugf("netns proc file for pid %d disappeared before we could read it: %v", pid, err) + return fourTuple{}, "", false + } + netns = fmt.Sprintf("%d", netnsIno) + + // find /proc/$pid/fd/$fd's ino + fdFilename := fmt.Sprintf("/proc/%d/fd/%d", pid, fd) + var statFdFile syscall.Stat_t + if err := fs.Stat(fdFilename, &statFdFile); err != nil { + log.Debugf("proc file %q disappeared before we could read it", fdFilename) + return fourTuple{}, "", false + } + + if statFdFile.Mode&syscall.S_IFMT != syscall.S_IFSOCK { + log.Errorf("file %q is not a socket", fdFilename) + return fourTuple{}, "", false + } + ino := statFdFile.Ino + + // read both /proc/pid/net/{tcp,tcp6} + buf := bytes.NewBuffer(make([]byte, 0, 5000)) + if _, err := procspy.ReadTCPFiles(pid, buf); err != nil { + log.Debugf("TCP proc file for pid %d disappeared before we could read it: %v", pid, err) + return fourTuple{}, "", false + } + + // find /proc/$pid/fd/$fd's ino in /proc/pid/net/tcp + pn := procspy.NewProcNet(buf.Bytes()) + for { + n := pn.Next() + if n == nil { + log.Debugf("connection for proc file %q not found. buf=%q", fdFilename, buf.String()) + break + } + if n.Inode == ino { + return fourTuple{n.LocalAddress.String(), n.RemoteAddress.String(), n.LocalPort, n.RemotePort}, netns, true + } + } + + return fourTuple{}, "", false +} + +func (t *EbpfTracker) handleFdInstall(ev tracer.EventType, pid int, fd int) { + tuple, netns, ok := tupleFromPidFd(pid, fd) + log.Debugf("EbpfTracker: got fd-install event: pid=%d fd=%d -> tuple=%s netns=%s ok=%v", pid, fd, tuple, netns, ok) + if !ok { + return + } + conn := ebpfConnection{ + incoming: true, + tuple: tuple, + pid: pid, + networkNamespace: netns, + } + t.openConnections[tuple.String()] = conn + if !process.IsProcInAccept("/proc", strconv.Itoa(pid)) { + t.tracer.RemoveFdInstallWatcher(uint32(pid)) + } +} + func (t *EbpfTracker) handleConnection(ev tracer.EventType, tuple fourTuple, pid int, networkNamespace string) { t.Lock() defer t.Unlock() @@ -160,6 +235,8 @@ func (t *EbpfTracker) handleConnection(ev tracer.EventType, tuple fourTuple, pid } else { log.Debugf("EbpfTracker: unmatched close event: %s pid=%d netns=%s", tuple.String(), pid, networkNamespace) } + default: + log.Debugf("EbpfTracker: unknown event: %s (%d)", ev, ev) } } @@ -178,7 +255,7 @@ func (t *EbpfTracker) walkConnections(f func(ebpfConnection)) { t.closedConnections = t.closedConnections[:0] } -func (t *EbpfTracker) feedInitialConnections(conns procspy.ConnIter, seenTuples map[string]fourTuple, hostNodeID string) { +func (t *EbpfTracker) feedInitialConnections(conns procspy.ConnIter, seenTuples map[string]fourTuple, processesWaitingInAccept []int, hostNodeID string) { t.readyToHandleConnections = true for conn := conns.Next(); conn != nil; conn = conns.Next() { var ( @@ -204,6 +281,10 @@ func (t *EbpfTracker) feedInitialConnections(conns procspy.ConnIter, seenTuples t.handleConnection(tracer.EventAccept, tuple, int(conn.Proc.PID), namespaceID) } } + for _, p := range processesWaitingInAccept { + t.tracer.AddFdInstallWatcher(uint32(p)) + log.Debugf("EbpfTracker: install fd-install watcher: pid=%d", p) + } } func (t *EbpfTracker) isReadyToHandleConnections() bool { diff --git a/probe/endpoint/procspy/proc_darwin.go b/probe/endpoint/procspy/proc_darwin.go new file mode 100644 index 0000000000..fca3c176d4 --- /dev/null +++ b/probe/endpoint/procspy/proc_darwin.go @@ -0,0 +1,16 @@ +package procspy + +import ( + "bytes" + "fmt" +) + +// ReadTCPFiles reads the proc files tcp and tcp6 for a pid +func ReadTCPFiles(pid int, buf *bytes.Buffer) (int64, error) { + return 0, fmt.Errorf("not supported on non-Linux systems") +} + +// ReadNetnsFromPID gets the netns inode of the specified pid +func ReadNetnsFromPID(pid int) (uint64, error) { + return 0, fmt.Errorf("not supported on non-Linux systems") +} diff --git a/probe/endpoint/procspy/proc_internal_test.go b/probe/endpoint/procspy/proc_internal_test.go index aeba25cf4e..8dc510c21f 100644 --- a/probe/endpoint/procspy/proc_internal_test.go +++ b/probe/endpoint/procspy/proc_internal_test.go @@ -62,7 +62,7 @@ func TestWalkProcPid(t *testing.T) { defer fs_hook.Restore() buf := bytes.Buffer{} - walker := process.NewWalker(procRoot) + walker := process.NewWalker(procRoot, false) ticker := time.NewTicker(time.Millisecond) defer ticker.Stop() pWalker := newPidWalker(walker, ticker.C, 1) diff --git a/probe/endpoint/procspy/proc_linux.go b/probe/endpoint/procspy/proc_linux.go index b6a88985ce..12c44fd0af 100644 --- a/probe/endpoint/procspy/proc_linux.go +++ b/probe/endpoint/procspy/proc_linux.go @@ -25,11 +25,10 @@ var ( ) type pidWalker struct { - walker process.Walker - tickc <-chan time.Time // Rate-limit clock. Sets the pace when traversing namespaces and /proc/PID/fd/* files. - stopc chan struct{} // Abort walk - fdBlockSize uint64 // Maximum number of /proc/PID/fd/* files to stat() per tick - netNamespacePathSuffix string + walker process.Walker + tickc <-chan time.Time // Rate-limit clock. Sets the pace when traversing namespaces and /proc/PID/fd/* files. + stopc chan struct{} // Abort walk + fdBlockSize uint64 // Maximum number of /proc/PID/fd/* files to stat() per tick } func newPidWalker(walker process.Walker, tickc <-chan time.Time, fdBlockSize uint64) pidWalker { @@ -38,7 +37,6 @@ func newPidWalker(walker process.Walker, tickc <-chan time.Time, fdBlockSize uin tickc: tickc, fdBlockSize: fdBlockSize, stopc: make(chan struct{}), - netNamespacePathSuffix: getNetNamespacePathSuffix(), } return w } @@ -91,10 +89,8 @@ func getNetNamespacePathSuffix() string { return netNamespacePathSuffix } -// Read the connections for a group of processes living in the same namespace, -// which are found (identically) in /proc/PID/net/tcp{,6} for any of the -// processes. -func readProcessConnections(buf *bytes.Buffer, namespaceProcs []*process.Process) (bool, error) { +// ReadTCPFiles reads the proc files tcp and tcp6 for a pid +func ReadTCPFiles(pid int, buf *bytes.Buffer) (int64, error) { var ( errRead error errRead6 error @@ -102,31 +98,42 @@ func readProcessConnections(buf *bytes.Buffer, namespaceProcs []*process.Process read6 int64 ) - for _, p := range namespaceProcs { - dirName := strconv.Itoa(p.PID) + // even for tcp4 connections, we need to read the "tcp6" file because of IPv4-Mapped IPv6 Addresses - read, errRead = readFile(filepath.Join(procRoot, dirName, "/net/tcp"), buf) - read6, errRead6 = readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf) + dirName := strconv.Itoa(pid) + read, errRead = readFile(filepath.Join(procRoot, dirName, "/net/tcp"), buf) + read6, errRead6 = readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf) + + if errRead != nil { + return read + read6, errRead + } + return read + read6, errRead6 +} - if errRead != nil || errRead6 != nil { +// Read the connections for a group of processes living in the same namespace, +// which are found (identically) in /proc/PID/net/tcp{,6} for any of the +// processes. +func readProcessConnections(buf *bytes.Buffer, namespaceProcs []*process.Process) (bool, error) { + var ( + read int64 + err error + ) + for _, p := range namespaceProcs { + read, err = ReadTCPFiles(p.PID, buf) + if err != nil { // try next process continue } // Return after succeeding on any process // (proc/PID/net/tcp and proc/PID/net/tcp6 are identical for all the processes in the same namespace) - return read+read6 > 0, nil + return read > 0, nil } - // It would be cool to have an "or" error combinator - if errRead != nil { - return false, errRead - } - if errRead6 != nil { - return false, errRead6 + if err != nil { + return false, err } return false, nil - } // walkNamespace does the work of walk for a single namespace @@ -199,6 +206,19 @@ func (w pidWalker) walkNamespace(namespaceID uint64, buf *bytes.Buffer, sockets return nil } +// ReadNetnsFromPID gets the netns inode of the specified pid +func ReadNetnsFromPID(pid int) (uint64, error) { + var statT syscall.Stat_t + + dirName := strconv.Itoa(pid) + netNamespacePath := filepath.Join(procRoot, dirName, getNetNamespacePathSuffix()) + if err := fs.Stat(netNamespacePath, &statT); err != nil { + return 0, err + } + + return statT.Ino, nil +} + // walk walks over all numerical (PID) /proc entries. It reads // /proc/PID/net/tcp{,6} for each namespace and sees if the ./fd/* files of each // process in that namespace are symlinks to sockets. Returns a map from socket @@ -207,7 +227,6 @@ func (w pidWalker) walk(buf *bytes.Buffer) (map[uint64]*Proc, error) { var ( sockets = map[uint64]*Proc{} // map socket inode -> process namespaces = map[uint64][]*process.Process{} // map network namespace id -> processes - statT syscall.Stat_t ) // We do two process traversals: One to group processes by namespace and @@ -219,14 +238,11 @@ func (w pidWalker) walk(buf *bytes.Buffer) (map[uint64]*Proc, error) { // the processes living in that namespace. w.walker.Walk(func(p, _ process.Process) { - dirName := strconv.Itoa(p.PID) - - netNamespacePath := filepath.Join(procRoot, dirName, w.netNamespacePathSuffix) - if err := fs.Stat(netNamespacePath, &statT); err != nil { + namespaceID, err := ReadNetnsFromPID(p.PID) + if err != nil { return } - namespaceID := statT.Ino namespaces[namespaceID] = append(namespaces[namespaceID], &p) }) diff --git a/probe/endpoint/procspy/procnet.go b/probe/endpoint/procspy/procnet.go index 3073ad2c3b..f2124687bd 100644 --- a/probe/endpoint/procspy/procnet.go +++ b/probe/endpoint/procspy/procnet.go @@ -63,12 +63,12 @@ again: p.c.LocalAddress, p.c.LocalPort = scanAddressNA(local, &p.bytesLocal) p.c.RemoteAddress, p.c.RemotePort = scanAddressNA(remote, &p.bytesRemote) - p.c.inode = parseDec(inode) + p.c.Inode = parseDec(inode) p.b = nextLine(b) - if _, alreadySeen := p.seen[p.c.inode]; alreadySeen { + if _, alreadySeen := p.seen[p.c.Inode]; alreadySeen { goto again } - p.seen[p.c.inode] = struct{}{} + p.seen[p.c.Inode] = struct{}{} return &p.c } diff --git a/probe/endpoint/procspy/procnet_internal_test.go b/probe/endpoint/procspy/procnet_internal_test.go index 8762826788..a991593f0b 100644 --- a/probe/endpoint/procspy/procnet_internal_test.go +++ b/probe/endpoint/procspy/procnet_internal_test.go @@ -20,28 +20,28 @@ func TestProcNet(t *testing.T) { LocalPort: 0xa6c0, RemoteAddress: net.IP([]byte{0, 0, 0, 0}), RemotePort: 0x0, - inode: 5107, + Inode: 5107, }, { LocalAddress: net.IP([]byte{0, 0, 0, 0}), LocalPort: 0x006f, RemoteAddress: net.IP([]byte{0, 0, 0, 0}), RemotePort: 0x0, - inode: 5084, + Inode: 5084, }, { LocalAddress: net.IP([]byte{0x7f, 0x0, 0x0, 0x01}), LocalPort: 0x0019, RemoteAddress: net.IP([]byte{0, 0, 0, 0}), RemotePort: 0x0, - inode: 10550, + Inode: 10550, }, { LocalAddress: net.IP([]byte{0x2e, 0xf6, 0x2c, 0xa1}), LocalPort: 0xe4d7, RemoteAddress: net.IP([]byte{0xc0, 0x1e, 0xfc, 0x57}), RemotePort: 0x01bb, - inode: 639474, + Inode: 639474, }, } for i := 0; i < 4; i++ { @@ -73,7 +73,7 @@ func TestTransport6(t *testing.T) { RemoteAddress: net.IP(make([]byte, 16)), RemotePort: 0x0, // uid: 0, - inode: 23661201, + Inode: 23661201, }, { // state: 1, @@ -92,7 +92,7 @@ func TestTransport6(t *testing.T) { }), RemotePort: 0x01bb, // uid: 1000, - inode: 36856710, + Inode: 36856710, }, } @@ -148,7 +148,7 @@ func TestProcNetFiltersDuplicates(t *testing.T) { LocalPort: 0xa6c0, RemoteAddress: net.IP([]byte{0, 0, 0, 0}), RemotePort: 0x0, - inode: 5107, + Inode: 5107, } have := p.Next() want := expected diff --git a/probe/endpoint/procspy/spy.go b/probe/endpoint/procspy/spy.go index b0459d8d43..04a9c3692b 100644 --- a/probe/endpoint/procspy/spy.go +++ b/probe/endpoint/procspy/spy.go @@ -22,7 +22,7 @@ type Connection struct { LocalPort uint16 RemoteAddress net.IP RemotePort uint16 - inode uint64 + Inode uint64 Proc Proc } diff --git a/probe/endpoint/procspy/spy_linux.go b/probe/endpoint/procspy/spy_linux.go index ec668a47d7..bdc80c66fc 100644 --- a/probe/endpoint/procspy/spy_linux.go +++ b/probe/endpoint/procspy/spy_linux.go @@ -26,7 +26,7 @@ func (c *pnConnIter) Next() *Connection { bufPool.Put(c.buf) return nil } - if proc, ok := c.procs[n.inode]; ok { + if proc, ok := c.procs[n.Inode]; ok { n.Proc = *proc } return n diff --git a/probe/endpoint/procspy/spy_linux_internal_test.go b/probe/endpoint/procspy/spy_linux_internal_test.go index 6f658c6f7a..d5e1fb1e1d 100644 --- a/probe/endpoint/procspy/spy_linux_internal_test.go +++ b/probe/endpoint/procspy/spy_linux_internal_test.go @@ -14,7 +14,7 @@ import ( func TestLinuxConnections(t *testing.T) { fs_hook.Mock(mockFS) defer fs_hook.Restore() - scanner := NewConnectionScanner(process.NewWalker("/proc")) + scanner := NewConnectionScanner(process.NewWalker("/proc", false)) defer scanner.Stop() // let the background scanner finish its first pass @@ -30,7 +30,7 @@ func TestLinuxConnections(t *testing.T) { LocalPort: 42688, RemoteAddress: net.ParseIP("0.0.0.0").To4(), RemotePort: 0, - inode: 5107, + Inode: 5107, Proc: Proc{ PID: 1, Name: "foo", diff --git a/probe/process/walker.go b/probe/process/walker.go index af77217be6..177c212056 100644 --- a/probe/process/walker.go +++ b/probe/process/walker.go @@ -4,15 +4,16 @@ import "sync" // Process represents a single process. type Process struct { - PID, PPID int - Name string - Cmdline string - Threads int - Jiffies uint64 - RSSBytes uint64 - RSSBytesLimit uint64 - OpenFilesCount int - OpenFilesLimit uint64 + PID, PPID int + Name string + Cmdline string + Threads int + Jiffies uint64 + RSSBytes uint64 + RSSBytesLimit uint64 + OpenFilesCount int + OpenFilesLimit uint64 + IsWaitingInAccept bool } // Walker is something that walks the /proc directory diff --git a/probe/process/walker_darwin.go b/probe/process/walker_darwin.go index 30ec022ed6..dd540f79ba 100644 --- a/probe/process/walker_darwin.go +++ b/probe/process/walker_darwin.go @@ -8,7 +8,7 @@ import ( ) // NewWalker returns a Darwin (lsof-based) walker. -func NewWalker(_ string) Walker { +func NewWalker(_ string, _ bool) Walker { return &walker{} } @@ -22,6 +22,13 @@ const ( // These functions copied from procspy. +// IsProcInAccept returns true if the process has a at least one thread +// blocked on the accept() system call +func IsProcInAccept(procRoot, pid string) (ret bool) { + // Not implemented on darwin + return false +} + func (walker) Walk(f func(Process, Process)) error { output, err := exec.Command( lsofBinary, diff --git a/probe/process/walker_linux.go b/probe/process/walker_linux.go index 2191044e31..05240bd971 100644 --- a/probe/process/walker_linux.go +++ b/probe/process/walker_linux.go @@ -17,7 +17,8 @@ import ( ) type walker struct { - procRoot string + procRoot string + gatheringWaitingInAccept bool } var ( @@ -38,8 +39,11 @@ const ( ) // NewWalker creates a new process Walker. -func NewWalker(procRoot string) Walker { - return &walker{procRoot: procRoot} +func NewWalker(procRoot string, gatheringWaitingInAccept bool) Walker { + return &walker{ + procRoot: procRoot, + gatheringWaitingInAccept: gatheringWaitingInAccept, + } } // skipNSpaces skips nSpaces in buf and updates the cursor 'pos' @@ -166,6 +170,30 @@ func (w *walker) readCmdline(filename string) (cmdline, name string) { return } +// IsProcInAccept returns true if the process has a at least one thread +// blocked on the accept() system call +func IsProcInAccept(procRoot, pid string) (ret bool) { + tasks, err := fs.ReadDirNames(path.Join(procRoot, pid, "task")) + if err != nil { + // if the process has terminated, it is obviously not blocking + // on the accept system call + return false + } + + for _, tid := range tasks { + buf, err := fs.ReadFile(path.Join(procRoot, pid, "task", tid, "wchan")) + if err != nil { + // if a thread has terminated, it is obviously not + // blocking on the accept system call + continue + } + if strings.TrimSpace(string(buf)) == "inet_csk_accept" { + return true + } + } + return false +} + // Walk walks the supplied directory (expecting it to look like /proc) // and marshalls the files into instances of Process, which it then // passes one-by-one to the supplied function. Walk is only made public @@ -215,17 +243,23 @@ func (w *walker) Walk(f func(Process, Process)) error { cmdlineCache.Set([]byte(filename), []byte(fmt.Sprintf("%s\x00%s", cmdline, name)), cmdlineCacheTimeout) } + isWaitingInAccept := false + if w.gatheringWaitingInAccept { + isWaitingInAccept = IsProcInAccept(w.procRoot, filename) + } + f(Process{ - PID: pid, - PPID: ppid, - Name: name, - Cmdline: cmdline, - Threads: threads, - Jiffies: jiffies, - RSSBytes: rss, - RSSBytesLimit: rssLimit, - OpenFilesCount: openFilesCount, - OpenFilesLimit: openFilesLimit, + PID: pid, + PPID: ppid, + Name: name, + Cmdline: cmdline, + Threads: threads, + Jiffies: jiffies, + RSSBytes: rss, + RSSBytesLimit: rssLimit, + OpenFilesCount: openFilesCount, + OpenFilesLimit: openFilesLimit, + IsWaitingInAccept: isWaitingInAccept, }, Process{}) } diff --git a/probe/process/walker_linux_test.go b/probe/process/walker_linux_test.go index 1da50deb79..cfa18d8d97 100644 --- a/probe/process/walker_linux_test.go +++ b/probe/process/walker_linux_test.go @@ -88,7 +88,7 @@ func TestWalker(t *testing.T) { } have := map[int]process.Process{} - walker := process.NewWalker("/proc") + walker := process.NewWalker("/proc", false) err := walker.Walk(func(p, _ process.Process) { have[p.PID] = p }) diff --git a/probe/process/walker_test.go b/probe/process/walker_test.go index c1287a0e2f..f71558991b 100644 --- a/probe/process/walker_test.go +++ b/probe/process/walker_test.go @@ -13,7 +13,7 @@ func TestBasicWalk(t *testing.T) { procRoot = "/proc" procFunc = func(process.Process, process.Process) {} ) - if err := process.NewWalker(procRoot).Walk(procFunc); err != nil { + if err := process.NewWalker(procRoot, false).Walk(procFunc); err != nil { t.Fatal(err) } } diff --git a/prog/probe.go b/prog/probe.go index 33c579cc45..1fe6d95e57 100644 --- a/prog/probe.go +++ b/prog/probe.go @@ -158,7 +158,7 @@ func probeMain(flags probeFlags, targets []appclient.Target) { var processCache *process.CachingWalker if flags.procEnabled { - processCache = process.NewCachingWalker(process.NewWalker(flags.procRoot)) + processCache = process.NewCachingWalker(process.NewWalker(flags.procRoot, false)) p.AddTicker(processCache) p.AddReporter(process.NewReporter(processCache, hostID, process.GetDeltaTotalJiffies, flags.noCommandLineArguments)) } From 4613819cb9e3cf5d03d1660ed5c159038981054c Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Tue, 16 May 2017 17:38:23 +0200 Subject: [PATCH 3/4] integration test: accept before kretprobe --- ..._container_accept_before_kretprobe_test.sh | 39 +++++++++++++++++++ integration/setup.sh | 1 + 2 files changed, 40 insertions(+) create mode 100755 integration/314_container_accept_before_kretprobe_test.sh diff --git a/integration/314_container_accept_before_kretprobe_test.sh b/integration/314_container_accept_before_kretprobe_test.sh new file mode 100755 index 0000000000..d87a4b866e --- /dev/null +++ b/integration/314_container_accept_before_kretprobe_test.sh @@ -0,0 +1,39 @@ +#! /bin/bash + +# shellcheck disable=SC1091 +. ./config.sh + +start_suite "Test accept before kretprobe, see https://github.com/weaveworks/tcptracer-bpf/issues/10" + +weave_on "$HOST1" launch + +# Launch the server before Scope to make sure it calls accept() before Scope's +# kretprobe on the accept function is installed. We use busybox' nc instead of +# Alpine's nc so that it blocks on the accept() syscall. +weave_on "$HOST1" run -d --name server busybox /bin/sh -c "while true; do \ + date ; + sleep 1 ; + done | nc -l -p 8080" + +scope_on "$HOST1" launch --probe.ebpf.connections=true +wait_for_containers "$HOST1" 60 server +has_container "$HOST1" server + +weave_on "$HOST1" run -d --name client busybox /bin/sh -c "ping -c 5 server.weave.local; \ + while true; do \ + date ; + sleep 1 ; + done | nc server.weave.local 8080" + +wait_for_containers "$HOST1" 60 server client + +has_container "$HOST1" client + +list_containers "$HOST1" +list_connections "$HOST1" + +has_connection containers "$HOST1" client server + +endpoints_have_ebpf "$HOST1" + +scope_end_suite diff --git a/integration/setup.sh b/integration/setup.sh index 2da3e605bb..78a22ae861 100755 --- a/integration/setup.sh +++ b/integration/setup.sh @@ -26,6 +26,7 @@ setup_host() { echo Prefetching Images on "$HOST" docker_on "$HOST" pull peterbourgon/tns-db docker_on "$HOST" pull alpine + docker_on "$HOST" pull busybox docker_on "$HOST" pull nginx } From b761b5e52b46222553f65e80d568f21da2480967 Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Wed, 17 May 2017 14:24:08 +0200 Subject: [PATCH 4/4] integration tests: better log messages --- integration/config.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/integration/config.sh b/integration/config.sh index 72eb51fdfa..ac7d37c21c 100644 --- a/integration/config.sh +++ b/integration/config.sh @@ -43,14 +43,15 @@ scope_end_suite() { list_containers() { local host=$1 echo "Listing containers on ${host}:" - curl -s "http://${host}:4040/api/topology/containers?system=show" | jq -r '.nodes[] | select(has("metadata")) | .metadata[] | select(.id == "docker_image_name") | .value' + curl -s "http://${host}:4040/api/topology/containers?system=show" | jq -r '.nodes[] | select(has("metadata")) | { "image": .metadata[] | select(.id == "docker_image_name") | .value, "label": .label, "id": .id} | .id + " (" + .image + ", " + .label + ")"' + echo } list_connections() { local host=$1 echo "Listing connections on ${host}:" - curl -s "http://${host}:4040/api/topology/containers?system=show" | jq -r '.nodes[] | select(has("adjacency")) | { "from": .id, "to": .adjacency[]} | .from + " -> " + .to' - + curl -s "http://${host}:4040/api/topology/containers?system=show" | jq -r '.nodes[] | select(has("adjacency")) | { "from_name": .label, "from_id": .id, "to": .adjacency[]} | .from_id + " (" + .from_name+ ") -> " + .to' + echo } # this checks we have a named node in the given view