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  }