github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/tchandler/tc.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  //go:build !withoutebpf
    16  
    17  package tchandler
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  
    24  	"github.com/cilium/ebpf"
    25  	"github.com/florianl/go-tc"
    26  	tccore "github.com/florianl/go-tc/core"
    27  	"golang.org/x/sys/unix"
    28  )
    29  
    30  const (
    31  	// filterInfo is a combination of priority and protocol: priority << 16 | proto. See
    32  	// https://github.com/iproute2/iproute2/blob/f443565f8df65e7d3b3e7cb5f4e94aec1e12d067/tc/tc_filter.c#L147
    33  	// https://github.com/iproute2/iproute2/blob/f443565f8df65e7d3b3e7cb5f4e94aec1e12d067/tc/tc_filter.c#L68
    34  	// - Protocol: The protocol this classifier will accept. We want to match all packets.
    35  	// Hence, use ETH_P_ALL.
    36  	// - Priority: The priority of this filter. We use 1 as we don't install other filters in
    37  	// the same interface.
    38  	filterInfo = uint32(0x1<<16 | 0x0300) // priority (1) << 16 | proto (htons(ETH_P_ALL))
    39  
    40  	filterHandleMax = 128
    41  )
    42  
    43  func ptr[T any](v T) *T {
    44  	return &v
    45  }
    46  
    47  type AttachmentDirection int
    48  
    49  const (
    50  	AttachmentDirectionUnspec AttachmentDirection = iota
    51  	AttachmentDirectionIngress
    52  	AttachmentDirectionEgress
    53  )
    54  
    55  // createClsActQdisc creates a clsact qdisc on the given interface.
    56  func createClsActQdisc(tcnl *tc.Tc, iface *net.Interface) (*tc.Object, error) {
    57  	// Install Qdisc on interface
    58  	qdisc := &tc.Object{
    59  		Msg: tc.Msg{
    60  			Family:  unix.AF_UNSPEC,
    61  			Ifindex: uint32(iface.Index),
    62  			Handle:  tccore.BuildHandle(tc.HandleRoot, 0),
    63  			Parent:  tc.HandleIngress,
    64  		},
    65  		Attribute: tc.Attribute{
    66  			Kind: "clsact",
    67  		},
    68  	}
    69  
    70  	if err := tcnl.Qdisc().Add(qdisc); err != nil {
    71  		return nil, fmt.Errorf("adding clsact qdisc to %s: %w", iface.Name, err)
    72  	}
    73  
    74  	return qdisc, nil
    75  }
    76  
    77  // addTCFilter adds a filter to the given interface. It returns the filter object or an error. In
    78  // order to allow multiple filters on the same interface, it tries to add the filter with different
    79  // handles until it succeeds or it has tried filterHandleMax times.
    80  func addTCFilter(tcnl *tc.Tc, prog *ebpf.Program, iface *net.Interface, dir AttachmentDirection) (*tc.Object, error) {
    81  	info, _ := prog.Info()
    82  	var parent uint32
    83  
    84  	switch dir {
    85  	case AttachmentDirectionIngress:
    86  		parent = tccore.BuildHandle(tc.HandleRoot, tc.HandleMinIngress)
    87  	case AttachmentDirectionEgress:
    88  		parent = tccore.BuildHandle(tc.HandleRoot, tc.HandleMinEgress)
    89  	default:
    90  		return nil, fmt.Errorf("invalid filter direction")
    91  	}
    92  
    93  	for handle := uint32(0x1); handle < filterHandleMax; handle++ {
    94  		filter := &tc.Object{
    95  			Msg: tc.Msg{
    96  				Family:  unix.AF_UNSPEC,
    97  				Ifindex: uint32(iface.Index),
    98  				Handle:  handle,
    99  				Parent:  parent,
   100  				Info:    filterInfo,
   101  			},
   102  			Attribute: tc.Attribute{
   103  				Kind: "bpf",
   104  				BPF: &tc.Bpf{
   105  					FD:    ptr(uint32(prog.FD())),
   106  					Name:  ptr(info.Name),
   107  					Flags: ptr(uint32(tc.BpfActDirect)),
   108  				},
   109  			},
   110  		}
   111  
   112  		if err := tcnl.Filter().Add(filter); err == nil {
   113  			return filter, nil
   114  		} else if !errors.Is(err, unix.EEXIST) {
   115  			return nil, err
   116  		}
   117  	}
   118  
   119  	return nil, fmt.Errorf("creating filter (too many tries)")
   120  }