github.com/cilium/ebpf@v0.10.0/examples/tcprtt/main.go (about)

     1  // This program demonstrates attaching a fentry eBPF program to
     2  // tcp_close and reading the RTT from the TCP socket using CO-RE helpers.
     3  // It prints the IPs/ports/RTT information
     4  // once the host closes a TCP connection.
     5  // It supports only IPv4 for this example.
     6  //
     7  // Sample output:
     8  //
     9  // examples# go run -exec sudo ./tcprtt
    10  // 2022/03/19 22:30:34 Src addr        Port   -> Dest addr       Port   RTT
    11  // 2022/03/19 22:30:36 10.0.1.205      50578  -> 117.102.109.186 5201   195
    12  // 2022/03/19 22:30:53 10.0.1.205      0      -> 89.84.1.178     9200   30
    13  // 2022/03/19 22:30:53 10.0.1.205      36022  -> 89.84.1.178     9200   28
    14  
    15  package main
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"errors"
    21  	"log"
    22  	"net"
    23  	"os"
    24  	"os/signal"
    25  	"syscall"
    26  
    27  	"github.com/cilium/ebpf/internal"
    28  	"github.com/cilium/ebpf/link"
    29  	"github.com/cilium/ebpf/ringbuf"
    30  	"github.com/cilium/ebpf/rlimit"
    31  )
    32  
    33  // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
    34  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type event bpf tcprtt.c -- -I../headers
    35  
    36  func main() {
    37  	stopper := make(chan os.Signal, 1)
    38  	signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
    39  
    40  	// Allow the current process to lock memory for eBPF resources.
    41  	if err := rlimit.RemoveMemlock(); err != nil {
    42  		log.Fatal(err)
    43  	}
    44  
    45  	// Load pre-compiled programs and maps into the kernel.
    46  	objs := bpfObjects{}
    47  	if err := loadBpfObjects(&objs, nil); err != nil {
    48  		log.Fatalf("loading objects: %v", err)
    49  	}
    50  	defer objs.Close()
    51  
    52  	link, err := link.AttachTracing(link.TracingOptions{
    53  		Program: objs.bpfPrograms.TcpClose,
    54  	})
    55  	if err != nil {
    56  		log.Fatal(err)
    57  	}
    58  	defer link.Close()
    59  
    60  	rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
    61  	if err != nil {
    62  		log.Fatalf("opening ringbuf reader: %s", err)
    63  	}
    64  	defer rd.Close()
    65  
    66  	log.Printf("%-15s %-6s -> %-15s %-6s %-6s",
    67  		"Src addr",
    68  		"Port",
    69  		"Dest addr",
    70  		"Port",
    71  		"RTT",
    72  	)
    73  	go readLoop(rd)
    74  
    75  	// Wait
    76  	<-stopper
    77  }
    78  
    79  func readLoop(rd *ringbuf.Reader) {
    80  	// bpfEvent is generated by bpf2go.
    81  	var event bpfEvent
    82  	for {
    83  		record, err := rd.Read()
    84  		if err != nil {
    85  			if errors.Is(err, ringbuf.ErrClosed) {
    86  				log.Println("received signal, exiting..")
    87  				return
    88  			}
    89  			log.Printf("reading from reader: %s", err)
    90  			continue
    91  		}
    92  
    93  		// Parse the ringbuf event entry into a bpfEvent structure.
    94  		if err := binary.Read(bytes.NewBuffer(record.RawSample), internal.NativeEndian, &event); err != nil {
    95  			log.Printf("parsing ringbuf event: %s", err)
    96  			continue
    97  		}
    98  
    99  		log.Printf("%-15s %-6d -> %-15s %-6d %-6d",
   100  			intToIP(event.Saddr),
   101  			event.Sport,
   102  			intToIP(event.Daddr),
   103  			event.Dport,
   104  			event.Srtt,
   105  		)
   106  	}
   107  }
   108  
   109  // intToIP converts IPv4 number to net.IP
   110  func intToIP(ipNum uint32) net.IP {
   111  	ip := make(net.IP, 4)
   112  	internal.NativeEndian.PutUint32(ip, ipNum)
   113  	return ip
   114  }