github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/formatters/formatters.go (about) 1 // Copyright 2024 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 package formatters 16 17 import ( 18 "encoding/binary" 19 "fmt" 20 "net" 21 "syscall" 22 "time" 23 24 "golang.org/x/sys/unix" 25 26 "github.com/inspektor-gadget/inspektor-gadget/pkg/datasource" 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api" 28 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/operators" 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/params" 31 ) 32 33 // Keep this aligned with include/gadget/types.h 34 const ( 35 // L3EndpointTypeName contains the name of the type that gadgets should use to store an L3 endpoint. 36 L3EndpointTypeName = "gadget_l3endpoint_t" 37 38 // L4EndpointTypeName contains the name of the type that gadgets should use to store an L4 endpoint. 39 L4EndpointTypeName = "gadget_l4endpoint_t" 40 41 // TimestampTypeName contains the name of the type to store a timestamp 42 TimestampTypeName = "gadget_timestamp" 43 44 // Name of the type to store a signal 45 SignalTypeName = "gadget_signal" 46 ) 47 48 type formattersOperator struct{} 49 50 func (f *formattersOperator) Name() string { 51 return "formatters" 52 } 53 54 func (f *formattersOperator) Init(params *params.Params) error { 55 return nil 56 } 57 58 func (f *formattersOperator) GlobalParams() api.Params { 59 return nil 60 } 61 62 func (f *formattersOperator) InstanceParams() api.Params { 63 return nil 64 } 65 66 func (f *formattersOperator) InstantiateDataOperator(gadgetCtx operators.GadgetContext, paramValues api.ParamValues) (operators.DataOperatorInstance, error) { 67 inst := &formattersOperatorInstance{ 68 converters: make(map[datasource.DataSource][]converter), 69 } 70 logger := gadgetCtx.Logger() 71 // Find things we can enrich 72 for _, ds := range gadgetCtx.GetDataSources() { 73 var converters []converter 74 logger.Debugf("formatterOperator inspecting datasource %q", ds.Name()) 75 for _, r := range replacers { 76 fields := ds.GetFieldsWithTag(r.selectors...) 77 if len(fields) == 0 { 78 continue 79 } 80 logger.Debugf("> found %d fields for replacer %v", len(fields), r.selectors) 81 for _, field := range fields { 82 replFunc, err := r.replace(ds, field) 83 if err != nil { 84 logger.Debugf("> skipping field %q: %v", field.Name(), err) 85 continue 86 } 87 if replFunc == nil { 88 continue 89 } 90 converters = append(converters, converter{ 91 name: r.name, 92 src: field, 93 replacer: replFunc, 94 priority: r.priority, 95 }) 96 } 97 } 98 if len(converters) > 0 { 99 inst.converters[ds] = converters 100 } 101 } 102 // Don't run, if we don't have anything to do 103 if len(inst.converters) == 0 { 104 return nil, nil 105 } 106 107 return inst, nil 108 } 109 110 type converter struct { 111 name string 112 src datasource.FieldAccessor 113 replacer func(datasource.Data) error 114 priority int 115 } 116 117 type replacer struct { 118 name string 119 120 // selectors describes which fields to look for 121 selectors []string 122 123 // replace will be called for incoming data with the source and target fields set 124 replace func(datasource.DataSource, datasource.FieldAccessor) (func(datasource.Data) error, error) 125 126 // priority to be used when subscribing to the DataSource 127 priority int 128 } 129 130 // careful: order and priority matter both! 131 var replacers = []replacer{ 132 { 133 name: "signal", 134 selectors: []string{"type:" + SignalTypeName}, 135 replace: func(ds datasource.DataSource, in datasource.FieldAccessor) (func(data datasource.Data) error, error) { 136 oldName := in.Name() 137 138 if err := in.Rename(oldName + "_raw"); err != nil { 139 return nil, fmt.Errorf("renaming field: %w", err) 140 } 141 in.SetHidden(true, false) 142 143 signalField, err := ds.AddField(oldName) 144 if err != nil { 145 return nil, err 146 } 147 return func(data datasource.Data) error { 148 inBytes := in.Get(data) 149 switch len(inBytes) { 150 default: 151 return nil 152 case 4: 153 signalNumber := in.Uint32(data) 154 signalName := unix.SignalName(syscall.Signal(signalNumber)) 155 signalField.Set(data, []byte(signalName)) 156 } 157 return nil 158 }, nil 159 }, 160 priority: 0, 161 }, 162 { 163 name: "timestamp", 164 selectors: []string{"type:" + TimestampTypeName}, 165 replace: func(ds datasource.DataSource, in datasource.FieldAccessor) (func(data datasource.Data) error, error) { 166 // Read annotations to allow user-defined behavior; this needs to be documented // TODO 167 annotations := in.Annotations() 168 169 // remove reference to old field for backwards compatibility 170 in.RemoveReference(false) 171 172 timestampFormat := "2006-01-02T15:04:05.000000000Z07:00" 173 if format := annotations["formatters.timestamp.format"]; format != "" { 174 timestampFormat = format 175 } 176 177 outName := in.Name() 178 if out := annotations["formatters.timestamp.target"]; out != "" { 179 outName = out 180 } 181 182 out, err := ds.AddField(outName) 183 if err != nil { 184 return nil, nil 185 } 186 187 return func(data datasource.Data) error { 188 inBytes := in.Get(data) 189 switch len(inBytes) { 190 default: 191 return nil 192 case 8: 193 // TODO: WallTimeFromBootTime() converts too much for this, create a new func that does less 194 correctedTime := gadgets.WallTimeFromBootTime(ds.ByteOrder().Uint64(inBytes)) 195 ds.ByteOrder().PutUint64(inBytes, uint64(correctedTime)) 196 t := time.Unix(0, int64(correctedTime)) 197 return out.Set(data, []byte(t.Format(timestampFormat))) 198 } 199 }, nil 200 }, 201 priority: 0, 202 }, 203 { 204 name: "l3endpoint", 205 selectors: []string{"type:" + L3EndpointTypeName}, 206 replace: func(ds datasource.DataSource, in datasource.FieldAccessor) (func(data datasource.Data) error, error) { 207 // We do some length checks in here - since we expect the in field to be part of an eBPF struct that 208 // is always sized statically, we can avoid checking the individual entries later on. 209 in.SetHidden(true, false) 210 ips := in.GetSubFieldsWithTag("type:gadget_ip_addr_t") 211 if len(ips) != 1 { 212 return nil, fmt.Errorf("expected %d gadget_ip_addr_t field, got %d", 1, len(ips)) 213 } 214 if ips[0].Size() != 16 { 215 return nil, fmt.Errorf("expected gadget_ip_addr_t to have 16 bytes") 216 } 217 versions := in.GetSubFieldsWithTag("name:version") 218 if len(versions) != 1 { 219 return nil, fmt.Errorf("expected exactly 1 version field") 220 } 221 out, err := in.AddSubField("string", datasource.WithTags("l3string")) 222 if err != nil { 223 return nil, fmt.Errorf("adding string field: %w", err) 224 } 225 // Hide padding 226 for _, f := range in.GetSubFieldsWithTag("name:pad") { 227 f.SetHidden(true, false) 228 } 229 return func(entry datasource.Data) error { 230 ip := ips[0].Get(entry) 231 v := versions[0].Get(entry) 232 if len(v) != 1 { 233 return nil 234 } 235 var err error 236 switch v[0] { 237 case 4: 238 err = out.Set(entry, []byte(net.IP(ip[:4]).String())) 239 case 6: 240 err = out.Set(entry, []byte(net.IP(ip).String())) 241 default: 242 return fmt.Errorf("invalid IP version for l3endpoint") 243 } 244 return err 245 }, nil 246 }, 247 priority: 0, 248 }, 249 { 250 name: "l4endpoint", 251 selectors: []string{"type:" + L4EndpointTypeName}, 252 replace: func(ds datasource.DataSource, in datasource.FieldAccessor) (func(data datasource.Data) error, error) { 253 // We do some length checks in here - since we expect the in field to be part of an eBPF struct that 254 // is always sized statically, we can avoid checking the individual entries later on. 255 in.SetHidden(true, false) 256 // Get accessors to required fields 257 ports := in.GetSubFieldsWithTag("name:port") 258 if len(ports) != 1 { 259 return nil, fmt.Errorf("expected exactly 1 port field") 260 } 261 if ports[0].Size() != 2 { 262 return nil, fmt.Errorf("port size expected to be 2 bytes") 263 } 264 l3 := in.GetSubFieldsWithTag("type:" + L3EndpointTypeName) 265 if len(l3) != 1 { 266 return nil, fmt.Errorf("expected exactly 1 l3endpoint field") 267 } 268 l3strings := l3[0].GetSubFieldsWithTag("l3string") 269 if len(l3strings) != 1 { 270 return nil, fmt.Errorf("expected exactly 1 l3string field") 271 } 272 273 // Hide l3 & subfields of l3 274 l3[0].SetHidden(true, true) 275 out, err := in.AddSubField("address") 276 if err != nil { 277 return nil, fmt.Errorf("adding string field: %w", err) 278 } 279 return func(entry datasource.Data) error { 280 port := binary.BigEndian.Uint16(ports[0].Get(entry)) 281 out.Set(entry, []byte(fmt.Sprintf("%s:%d", string(l3strings[0].Get(entry)), port))) 282 return nil 283 }, nil 284 }, 285 priority: 1, 286 }, 287 } 288 289 func (f *formattersOperator) Priority() int { 290 return 0 291 } 292 293 type formattersOperatorInstance struct { 294 converters map[datasource.DataSource][]converter 295 } 296 297 func (f *formattersOperatorInstance) Name() string { 298 return "formatters" 299 } 300 301 func (f *formattersOperatorInstance) PreStart(gadgetCtx operators.GadgetContext) error { 302 for ds, converters := range f.converters { 303 for _, c := range converters { 304 conv := c 305 ds.Subscribe(func(ds datasource.DataSource, data datasource.Data) error { 306 return conv.replacer(data) 307 }, conv.priority) 308 } 309 } 310 return nil 311 } 312 313 func (f *formattersOperatorInstance) Start(gadgetCtx operators.GadgetContext) error { 314 return nil 315 } 316 317 func (f *formattersOperatorInstance) Stop(gadgetCtx operators.GadgetContext) error { 318 return nil 319 } 320 321 func init() { 322 operators.RegisterDataOperator(&formattersOperator{}) 323 }