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 }