github.com/jayanthvn/pure-gobpf@v0.0.0-20230623131354-8d1d959d9e0b/pkg/ebpf_tc/tc_probes.go (about) 1 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 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 ebpf_tc 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/jayanthvn/pure-gobpf/pkg/logger" 22 "github.com/vishvananda/netlink" 23 "golang.org/x/sys/unix" 24 ) 25 26 var log = logger.Get() 27 28 func enableQdisc(link netlink.Link) bool { 29 //var log = logger.Get() 30 log.Infof("Check if qdisc has to be enabled") 31 qdiscs, err := netlink.QdiscList(link) 32 if err != nil { 33 log.Infof("Unable to check qdisc hence try installing") 34 return true 35 } 36 37 qdiscHandle := netlink.MakeHandle(0xffff, 0) 38 for _, qdisc := range qdiscs { 39 attrs := qdisc.Attrs() 40 if attrs.LinkIndex != link.Attrs().Index { 41 continue 42 } 43 if (attrs.Handle&qdiscHandle) == qdiscHandle && attrs.Parent == netlink.HANDLE_INGRESS { 44 log.Infof("Found qdisc hence don't install again") 45 return false 46 } 47 } 48 log.Infof("Qdisc is not enabled hence install") 49 return true 50 51 } 52 53 func TCIngressAttach(interfaceName string, progFD int) error { 54 //var log = logger.Get() 55 intf, err := netlink.LinkByName(interfaceName) 56 if err != nil { 57 log.Infof("Failed to find device name") 58 return fmt.Errorf("failed to find device by name %s: %w", interfaceName, err) 59 } 60 61 attrs := netlink.QdiscAttrs{ 62 LinkIndex: intf.Attrs().Index, 63 Handle: netlink.MakeHandle(0xffff, 0), 64 Parent: netlink.HANDLE_INGRESS, 65 } 66 67 if enableQdisc(intf) { 68 qdisc := &netlink.GenericQdisc{ 69 QdiscAttrs: attrs, 70 QdiscType: "clsact", 71 } 72 73 if err := netlink.QdiscAdd(qdisc); err != nil { 74 log.Infof("Cannot add clsact") 75 return fmt.Errorf("cannot add clsact qdisc: %v", err) 76 } 77 } 78 79 // construct the filter 80 filter := &netlink.BpfFilter{ 81 FilterAttrs: netlink.FilterAttrs{ 82 LinkIndex: attrs.LinkIndex, 83 Parent: uint32(netlink.HANDLE_MIN_INGRESS), 84 Handle: 0x1, 85 Protocol: unix.ETH_P_ALL, 86 Priority: 1, 87 }, 88 Fd: progFD, 89 Name: "handle_ingress", 90 DirectAction: true, 91 } 92 93 if err = netlink.FilterAdd(filter); err != nil { 94 log.Infof("while loading egress program %q on fd %d: %v", "handle ingress", progFD, err) 95 return fmt.Errorf("while loading egress program %q on fd %d: %v", "handle ingress", progFD, err) 96 } 97 log.Infof("TC filter add done") 98 return nil 99 } 100 101 func TCIngressDetach(interfaceName string) error { 102 //var log = logger.Get() 103 intf, err := netlink.LinkByName(interfaceName) 104 if err != nil { 105 log.Infof("Failed to find device name") 106 return fmt.Errorf("failed to find device by name %s: %w", interfaceName, err) 107 } 108 109 //Currently supports only one handle, in future we might need to cache the handle 110 filterHandle := uint32(0x1) 111 filterParent := uint32(netlink.HANDLE_MIN_INGRESS) 112 113 filters, err := netlink.FilterList(intf, filterParent) 114 if err != nil { 115 log.Infof("Failed to filter list") 116 return fmt.Errorf("Failed to get filter list: %v", err) 117 } 118 119 for _, filter := range filters { 120 if filter.Attrs().Handle == filterHandle { 121 err = netlink.FilterDel(filter) 122 if err != nil { 123 return fmt.Errorf("Delete filter failed on intf %s : %v", interfaceName, err) 124 } 125 log.Infof("TC filter detach done") 126 return nil 127 } 128 } 129 return fmt.Errorf("Detach failed on ingress interface - %s", interfaceName) 130 } 131 132 func TCEgressAttach(interfaceName string, progFD int) error { 133 //var log = logger.Get() 134 intf, err := netlink.LinkByName(interfaceName) 135 if err != nil { 136 log.Infof("Failed to find device name") 137 return fmt.Errorf("failed to find device by name %s: %w", interfaceName, err) 138 } 139 140 attrs := netlink.QdiscAttrs{ 141 LinkIndex: intf.Attrs().Index, 142 Handle: netlink.MakeHandle(0xffff, 0), 143 Parent: netlink.HANDLE_INGRESS, 144 } 145 146 if enableQdisc(intf) { 147 qdisc := &netlink.GenericQdisc{ 148 QdiscAttrs: attrs, 149 QdiscType: "clsact", 150 } 151 152 if err := netlink.QdiscAdd(qdisc); err != nil { 153 log.Infof("Cannot add clsact") 154 return fmt.Errorf("cannot add clsact qdisc: %v", err) 155 } 156 } 157 158 // construct the filter 159 filter := &netlink.BpfFilter{ 160 FilterAttrs: netlink.FilterAttrs{ 161 LinkIndex: attrs.LinkIndex, 162 Parent: uint32(netlink.HANDLE_MIN_EGRESS), 163 Handle: 0x1, 164 Protocol: unix.ETH_P_ALL, 165 Priority: 1, 166 }, 167 Fd: progFD, 168 Name: "handle_egress", 169 DirectAction: true, 170 } 171 172 if err = netlink.FilterAdd(filter); err != nil { 173 log.Infof("while loading egress program %q on fd %d: %v", "handle egress", progFD, err) 174 return fmt.Errorf("while loading egress program %q on fd %d: %v", "handle egress", progFD, err) 175 } 176 log.Infof("TC filter add done") 177 return nil 178 } 179 180 func TCEgressDetach(interfaceName string) error { 181 //var log = logger.Get() 182 intf, err := netlink.LinkByName(interfaceName) 183 if err != nil { 184 log.Infof("Failed to find device name") 185 return fmt.Errorf("failed to find device by name %s: %w", interfaceName, err) 186 } 187 188 //Currently supports only one handle, in future we might need to cache the handle 189 filterHandle := uint32(0x1) 190 filterParent := uint32(netlink.HANDLE_MIN_EGRESS) 191 192 filters, err := netlink.FilterList(intf, filterParent) 193 if err != nil { 194 log.Infof("Failed to filter list") 195 return fmt.Errorf("Failed to get filter list: %v", err) 196 } 197 198 for _, filter := range filters { 199 if filter.Attrs().Handle == filterHandle { 200 err = netlink.FilterDel(filter) 201 if err != nil { 202 return fmt.Errorf("Delete filter failed on intf %s : %v", interfaceName, err) 203 } 204 log.Infof("TC filter detach done") 205 return nil 206 } 207 } 208 return fmt.Errorf("Detach failed on egress interface - %s", interfaceName) 209 } 210 211 func CleanupQdiscs(prefix string, ingressCleanup bool, egressCleanup bool) error { 212 //var log = logger.Get() 213 214 if prefix == "" { 215 log.Infof("Prefix should be given") 216 return nil 217 } 218 219 linkList, err := netlink.LinkList() 220 if err != nil { 221 log.Infof("Unable to get link list") 222 return err 223 } 224 225 for _, link := range linkList { 226 linkName := link.Attrs().Name 227 if strings.HasPrefix(linkName, prefix) { 228 if ingressCleanup { 229 log.Infof("Trying to cleanup ingress on %s", linkName) 230 err = TCIngressDetach(linkName) 231 if err != nil { 232 log.Infof("Failed to detach ingress, might not be present so moving on") 233 } 234 } 235 236 if egressCleanup { 237 log.Infof("Trying to cleanup egress on %s", linkName) 238 err = TCEgressDetach(linkName) 239 if err != nil { 240 log.Infof("Failed to detach egress, might not be present so moving on") 241 } 242 } 243 } 244 } 245 return nil 246 }