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  }