github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/profile/tcprtt/tracer/tracer.go (about) 1 // Copyright 2023 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build !withoutebpf 16 17 package tracer 18 19 import ( 20 "encoding/binary" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "strconv" 25 "unsafe" 26 27 "github.com/cilium/ebpf" 28 "github.com/cilium/ebpf/link" 29 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen" 31 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 33 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/tcprtt/types" 34 "github.com/inspektor-gadget/inspektor-gadget/pkg/histogram" 35 "github.com/inspektor-gadget/inspektor-gadget/pkg/logger" 36 "github.com/inspektor-gadget/inspektor-gadget/pkg/params" 37 ) 38 39 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -type hist -cc clang -cflags ${CFLAGS} tcpRTT ./bpf/tcprtt.bpf.c -- -I./bpf/ 40 41 type Config struct { 42 useMilliseconds bool 43 localAddrHist bool 44 remoteAddrHist bool 45 filterLocalPort uint16 46 filterRemotePort uint16 47 filterLocalAddress uint32 48 filterRemoteAddress uint32 49 filterLocalAddressV6 [16]byte 50 filterRemoteAddressV6 [16]byte 51 } 52 53 type Tracer struct { 54 objs tcpRTTObjects 55 tcpRcvEstKprobeLink link.Link 56 57 config *Config 58 logger logger.Logger 59 } 60 61 func (t *Tracer) RunWithResult(gadgetCtx gadgets.GadgetContext) ([]byte, error) { 62 t.logger = gadgetCtx.Logger() 63 64 if err := t.parseParams(gadgetCtx.GadgetParams()); err != nil { 65 return nil, fmt.Errorf("parsing parameters: %w", err) 66 } 67 68 defer t.close() 69 if err := t.install(); err != nil { 70 return nil, fmt.Errorf("installing tracer: %w", err) 71 } 72 73 gadgetcontext.WaitForTimeoutOrDone(gadgetCtx) 74 75 result, err := t.collectResult() 76 if err != nil { 77 return nil, fmt.Errorf("collecting result: %w", err) 78 } 79 return result, nil 80 } 81 82 // htons converts an unsigned short integer from host byte order to network byte order. 83 func htons(i uint16) uint16 { 84 b := make([]byte, 2) 85 binary.BigEndian.PutUint16(b, i) 86 return *(*uint16)(unsafe.Pointer(&b[0])) 87 } 88 89 func (t *Tracer) parseParams(params *params.Params) error { 90 t.config.useMilliseconds = params.Get(ParamMilliseconds).AsBool() 91 92 t.config.localAddrHist = params.Get(ParamByLocalAddress).AsBool() 93 t.config.remoteAddrHist = params.Get(ParamByRemoteAddress).AsBool() 94 if t.config.localAddrHist && t.config.remoteAddrHist { 95 return fmt.Errorf("local and remote address histograms cannot be enabled at the same time") 96 } 97 98 lPort := params.Get(ParamFilterLocalPort).AsString() 99 if lPort != "" { 100 p, err := strconv.ParseUint(lPort, 10, 16) 101 if err != nil { 102 return fmt.Errorf("parsing local port: %w", err) 103 } 104 t.config.filterLocalPort = uint16(p) 105 } 106 107 rPort := params.Get(ParamFilterRemotePort).AsString() 108 if rPort != "" { 109 p, err := strconv.ParseUint(rPort, 10, 16) 110 if err != nil { 111 return fmt.Errorf("parsing remote port: %w", err) 112 } 113 t.config.filterRemotePort = uint16(p) 114 } 115 116 lAddr := params.Get(ParamFilterLocalAddress).AsString() 117 if lAddr != "" { 118 l, err := gadgets.IPStringToUint32(lAddr) 119 if err != nil { 120 return fmt.Errorf("parsing local address: %w", err) 121 } 122 t.config.filterLocalAddress = l 123 } 124 125 rAddr := params.Get(ParamFilterRemoteAddress).AsString() 126 if rAddr != "" { 127 r, err := gadgets.IPStringToUint32(rAddr) 128 if err != nil { 129 return fmt.Errorf("parsing remote address: %w", err) 130 } 131 t.config.filterRemoteAddress = r 132 } 133 134 lAddrV6 := params.Get(ParamFilterLocalAddressV6).AsString() 135 if lAddrV6 != "" { 136 if lAddr != "" || rAddr != "" { 137 return errors.New("filtering by any IPv4 and local IPv6 is not permitted") 138 } 139 l, err := gadgets.IPStringToByteArray(lAddrV6) 140 if err != nil { 141 return fmt.Errorf("parsing local address: %w", err) 142 } 143 t.config.filterLocalAddressV6 = l 144 } 145 146 rAddrV6 := params.Get(ParamFilterRemoteAddressV6).AsString() 147 if rAddrV6 != "" { 148 if rAddr != "" || lAddr != "" { 149 return errors.New("filtering by any IPv4 and remote IPv6 is not permitted") 150 } 151 r, err := gadgets.IPStringToByteArray(rAddrV6) 152 if err != nil { 153 return fmt.Errorf("parsing remote address: %w", err) 154 } 155 t.config.filterRemoteAddressV6 = r 156 } 157 158 return nil 159 } 160 161 func (t *Tracer) collectResult() ([]byte, error) { 162 histsMap := t.objs.Hists 163 164 var key tcpRTTHistKey 165 if err := histsMap.NextKey(nil, unsafe.Pointer(&key)); err != nil { 166 if errors.Is(err, ebpf.ErrKeyNotExist) { 167 return nil, fmt.Errorf("no data was collected to generate the histogram") 168 } 169 return nil, fmt.Errorf("getting first histogram key: %w", err) 170 } 171 172 var unit histogram.Unit 173 if t.config.useMilliseconds { 174 unit = histogram.UnitMilliseconds 175 } else { 176 unit = histogram.UnitMicroseconds 177 } 178 179 var addressType types.AddressType 180 if t.config.localAddrHist { 181 addressType = types.AddressTypeLocal 182 } else if t.config.remoteAddrHist { 183 addressType = types.AddressTypeRemote 184 } else { 185 addressType = types.AddressTypeAll 186 } 187 188 report := types.Report{ 189 Histograms: make([]*types.ExtendedHistogram, 0), 190 } 191 192 var prev tcpRTTHistKey 193 for { 194 var addr string 195 if addressType == types.AddressTypeAll { 196 addr = types.WildcardAddress 197 } else { 198 addr = gadgets.IPStringFromBytes(key.Addr, gadgets.IPVerFromAF(key.Family)) 199 } 200 201 hist := tcpRTTHist{} 202 if err := histsMap.Lookup(key, unsafe.Pointer(&hist)); err != nil { 203 return nil, fmt.Errorf("getting data for histogram key %d (%s): %w", key, addr, err) 204 } 205 206 var avg float64 207 if hist.Cnt > 0 { 208 avg = float64(hist.Latency) / float64(hist.Cnt) 209 } 210 211 h := types.NewHistogram(unit, hist.Slots[:], addressType, addr, avg, t.config.filterLocalPort, t.config.filterRemotePort) 212 report.Histograms = append(report.Histograms, h) 213 214 prev = key 215 if err := histsMap.NextKey(unsafe.Pointer(&prev), unsafe.Pointer(&key)); err != nil { 216 if errors.Is(err, ebpf.ErrKeyNotExist) { 217 break 218 } 219 return nil, fmt.Errorf("getting next histogram key: %w", err) 220 } 221 } 222 223 return json.Marshal(report) 224 } 225 226 func (t *Tracer) close() { 227 t.tcpRcvEstKprobeLink = gadgets.CloseLink(t.tcpRcvEstKprobeLink) 228 229 t.objs.Close() 230 } 231 232 func (t *Tracer) install() error { 233 var spec *ebpf.CollectionSpec 234 235 spec, err := loadTcpRTT() 236 if err != nil { 237 return fmt.Errorf("loading specs: %w", err) 238 } 239 240 consts := map[string]interface{}{ 241 "targ_ms": t.config.useMilliseconds, 242 "targ_laddr_hist": t.config.localAddrHist, 243 "targ_raddr_hist": t.config.remoteAddrHist, 244 "targ_sport": htons(t.config.filterLocalPort), 245 "targ_dport": htons(t.config.filterRemotePort), 246 "targ_saddr": t.config.filterLocalAddress, 247 "targ_daddr": t.config.filterRemoteAddress, 248 "targ_saddr_v6": t.config.filterLocalAddressV6, 249 "targ_daddr_v6": t.config.filterRemoteAddressV6, 250 } 251 252 if err := spec.RewriteConstants(consts); err != nil { 253 return fmt.Errorf("rewriting constants: %w", err) 254 } 255 256 opts := ebpf.CollectionOptions{ 257 Programs: ebpf.ProgramOptions{ 258 KernelTypes: btfgen.GetBTFSpec(), 259 }, 260 } 261 262 if err := spec.LoadAndAssign(&t.objs, &opts); err != nil { 263 return fmt.Errorf("loading ebpf program: %w", err) 264 } 265 266 tcpRcvEstKprobeLink, err := link.Kprobe("tcp_rcv_established", t.objs.IgTcprcvestKp, nil) 267 if err != nil { 268 return fmt.Errorf("attaching kprobe: %w", err) 269 } 270 t.tcpRcvEstKprobeLink = tcpRcvEstKprobeLink 271 272 return nil 273 } 274 275 func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) { 276 return &Tracer{ 277 config: &Config{}, 278 }, nil 279 }