@@ -20,19 +20,14 @@ This works by tracing the tcp_v4_connect() and tcp_v6_connect() kernel functions
2020package main
2121
2222import (
23- "bytes"
24- "encoding/binary"
23+ "context"
2524 "flag"
26- "fmt"
27- "io"
2825 "log"
29- "net"
3026 "os"
3127 "os/signal"
3228 "syscall"
3329
3430 "github.com/cilium/ebpf/link"
35- "github.com/cilium/ebpf/perf"
3631 "golang.org/x/sys/unix"
3732)
3833
@@ -47,6 +42,7 @@ func main() {
4742 var (
4843 printTimestamp = flag .Bool ("timestamp" , false , "include the time of the connect in seconds on output, counting from the first event seen" )
4944 printUID = flag .Bool ("print-uid" , false , "include UID on output" )
45+ printCount = flag .Bool ("count" , false , "count connects per source IP and destination IP/port" )
5046 filterUID = flag .Int ("uid" , - 1 , "trace this UID only" )
5147 filterPID = flag .Int ("pid" , 0 , "trace this PID only" )
5248 )
@@ -62,13 +58,17 @@ func main() {
6258 return
6359 }
6460
65- // Replace constants in the BPF C program to filter connections by PID or UID.
6661 spec , err := LoadTCPConnect ()
6762 if err != nil {
6863 log .Printf ("failed to load collection spec: %v" , err )
6964 return
7065 }
66+
67+ // Replace constants in the BPF C program to filter connections by PID or UID.
7168 bpfConst := make (map [string ]interface {})
69+ if * printCount {
70+ bpfConst ["do_count" ] = * printCount
71+ }
7272 if * filterUID > - 1 {
7373 bpfConst ["filter_uid" ] = uint32 (* filterUID )
7474 }
@@ -115,124 +115,41 @@ func main() {
115115 }
116116 defer tcpv6krp .Close ()
117117
118- // Open a perf event reader from userspace on the PERF_EVENT_ARRAY map
119- // defined in the BPF C program.
120- rd , err := perf .NewReader (objs .TCPConnectMaps .Events , os .Getpagesize ())
121- if err != nil {
122- log .Printf ("creating perf event reader: %s" , err )
123- return
124- }
125- defer rd .Close ()
126-
127- sig := make (chan os.Signal , 1 )
128- signal .Notify (sig , syscall .SIGINT , syscall .SIGTERM )
129- go func () {
130- <- sig
131- rd .Close ()
132- }()
133-
134- printHeader (os .Stdout , * printTimestamp , * printUID )
135-
136- var startTimestamp float64
137- for {
138- record , err := rd .Read ()
139- if err != nil {
140- if perf .IsClosed (err ) {
141- break
142- }
143- log .Printf ("reading from perf event reader: %v" , err )
144- }
118+ ctx , stop := signal .NotifyContext (context .Background (), syscall .SIGINT , syscall .SIGTERM )
119+ defer stop ()
145120
146- if record .LostSamples != 0 {
147- log .Printf ("ring event perf buffer is full, dropped %d samples" , record .LostSamples )
148- continue
121+ if * printCount {
122+ var (
123+ ipv4key ipv4FlowKey
124+ ipv4value uint64
125+ )
126+ entries := objs .TCPConnectMaps .Ipv4Count .Iterate ()
127+ for entries .Next (& ipv4key , & ipv4value ) {
128+ log .Println (ipv4key , ipv4value )
129+ }
130+ if err := entries .Err (); err != nil {
131+ panic (err )
149132 }
150133
151- var e event
152- err = binary .Read (
153- bytes .NewBuffer (record .RawSample ),
154- binary .LittleEndian ,
155- & e ,
134+ var (
135+ ipv6key ipv6FlowKey
136+ ipv6value uint64
156137 )
157- if err != nil {
158- log . Printf ( "failed to parse perf event: %#+v" , err )
159- continue
138+ entries = objs . TCPConnectMaps . Ipv6Count . Iterate ()
139+ for entries . Next ( & ipv6key , & ipv6value ) {
140+ log . Println ( ipv6key , ipv6value )
160141 }
161-
162- if startTimestamp == 0 {
163- startTimestamp = float64 (e .Timestamp )
142+ if err := entries .Err (); err != nil {
143+ panic (err )
164144 }
165-
166- printEvent (os .Stdout , & e , startTimestamp , * printTimestamp , * printUID )
145+ // if err := objs.TCPConnectMaps.Ipv4Count.Lookup(&key, &value); err != nil {
146+ // log.Printf("failed to read IP v4 map: %s", err)
147+ // return
148+ // }
149+ } else {
150+ printEvents (ctx , objs .TCPConnectMaps .Events , * printTimestamp , * printUID )
167151 }
168152
169153 // The program terminates successfully if it received INT/TERM signal.
170154 exitCode = 0
171155}
172-
173- // event represents a perf event sent to userspace from the BPF program running in the kernel.
174- // Note, that it must match the C event struct, and both C and Go structs must be aligned the same way.
175- type event struct {
176- // SrcAddr is the source address.
177- SrcAddr [16 ]byte
178- // DstAddr is the destination address.
179- DstAddr [16 ]byte
180- // Comm is the process name that opened the connection.
181- Comm [16 ]byte
182- // Timestamp is the timestamp in microseconds.
183- Timestamp uint64
184- // AddrFam is the address family, 2 is AF_INET (IPv4), 10 is AF_INET6 (IPv6).
185- AddrFam uint32
186- // PID is the process ID that opened the connection.
187- PID uint32
188- // UID is the process user ID.
189- UID uint32
190- // DstPort is the destination port (uint16 in C struct).
191- // Note, network byte order is big endian.
192- DstPort [2 ]byte
193- }
194-
195- func printHeader (w io.Writer , printTimestamp , printUID bool ) {
196- if printTimestamp {
197- fmt .Fprintf (w , "%-9s" , "TIME(s)" )
198- }
199- if printUID {
200- fmt .Fprintf (w , "%-6s" , "UID" )
201- }
202- fmt .Fprintf (w , "%-6s %-12s %-2s %-16s %-16s %s\n " , "PID" , "COMM" , "IP" , "SADDR" , "DADDR" , "DPORT" )
203- }
204-
205- func printEvent (w io.Writer , e * event , startTimestamp float64 , printTimestamp , printUID bool ) {
206- var (
207- srcAddr , dstAddr net.IP
208- ipVer byte
209- )
210- switch e .AddrFam {
211- case 2 :
212- srcAddr = net .IP (e .SrcAddr [:4 ])
213- dstAddr = net .IP (e .DstAddr [:4 ])
214- ipVer = 4
215- case 10 :
216- srcAddr = net .IP (e .SrcAddr [:])
217- dstAddr = net .IP (e .DstAddr [:])
218- ipVer = 6
219- }
220-
221- if printTimestamp {
222- fmt .Fprintf (w , "%-9.3f" , (float64 (e .Timestamp )- startTimestamp )/ 1e6 )
223- }
224- if printUID {
225- fmt .Fprintf (w , "%-6d" , e .UID )
226- }
227-
228- fmt .Fprintf (
229- w ,
230- "%-6d %-12s %-2d %-16s %-16s %d\n " ,
231- e .PID ,
232- bytes .TrimRight (e .Comm [:], "\x00 " ),
233- ipVer ,
234- srcAddr ,
235- dstAddr ,
236- binary .BigEndian .Uint16 (e .DstPort [:]),
237- )
238- }
0 commit comments