github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/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/link"
    28  	"github.com/cilium/ebpf/ringbuf"
    29  	"github.com/cilium/ebpf/rlimit"
    30  )
    31  
    32  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type event bpf tcprtt.c -- -I../headers
    33  
    34  func main() {
    35  	stopper := make(chan os.Signal, 1)
    36  	signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
    37  
    38  	// Allow the current process to lock memory for eBPF resources.
    39  	if err := rlimit.RemoveMemlock(); err != nil {
    40  		log.Fatal(err)
    41  	}
    42  
    43  	// Load pre-compiled programs and maps into the kernel.
    44  	objs := bpfObjects{}
    45  	if err := loadBpfObjects(&objs, nil); err != nil {
    46  		log.Fatalf("loading objects: %v", err)
    47  	}
    48  	defer objs.Close()
    49  
    50  	link, err := link.AttachTracing(link.TracingOptions{
    51  		Program: objs.bpfPrograms.TcpClose,
    52  	})
    53  	if err != nil {
    54  		log.Fatal(err)
    55  	}
    56  	defer link.Close()
    57  
    58  	rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
    59  	if err != nil {
    60  		log.Fatalf("opening ringbuf reader: %s", err)
    61  	}
    62  	defer rd.Close()
    63  
    64  	log.Printf("%-15s %-6s -> %-15s %-6s %-6s",
    65  		"Src addr",
    66  		"Port",
    67  		"Dest addr",
    68  		"Port",
    69  		"RTT",
    70  	)
    71  	go readLoop(rd)
    72  
    73  	// Wait
    74  	<-stopper
    75  }
    76  
    77  func readLoop(rd *ringbuf.Reader) {
    78  	// bpfEvent is generated by bpf2go.
    79  	var event bpfEvent
    80  	for {
    81  		record, err := rd.Read()
    82  		if err != nil {
    83  			if errors.Is(err, ringbuf.ErrClosed) {
    84  				log.Println("received signal, exiting..")
    85  				return
    86  			}
    87  			log.Printf("reading from reader: %s", err)
    88  			continue
    89  		}
    90  
    91  		// Parse the ringbuf event entry into a bpfEvent structure.
    92  		if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.NativeEndian, &event); err != nil {
    93  			log.Printf("parsing ringbuf event: %s", err)
    94  			continue
    95  		}
    96  
    97  		log.Printf("%-15s %-6d -> %-15s %-6d %-6d",
    98  			intToIP(event.Saddr),
    99  			event.Sport,
   100  			intToIP(event.Daddr),
   101  			event.Dport,
   102  			event.Srtt,
   103  		)
   104  	}
   105  }
   106  
   107  // intToIP converts IPv4 number to net.IP
   108  func intToIP(ipNum uint32) net.IP {
   109  	ip := make(net.IP, 4)
   110  	binary.NativeEndian.PutUint32(ip, ipNum)
   111  	return ip
   112  }