github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/program_socket_filter.go (about)

     1  package gobpfld
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"syscall"
     7  
     8  	"github.com/dylandreimerink/gobpfld/bpfsys"
     9  	"github.com/dylandreimerink/gobpfld/bpftypes"
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  var _ BPFProgram = (*ProgramSocketFilter)(nil)
    14  
    15  type ProgramSocketFilter struct {
    16  	AbstractBPFProgram
    17  
    18  	AttachedSocketFDs []int
    19  }
    20  
    21  type ProgSKFilterLoadOpts struct {
    22  	VerifierLogLevel bpftypes.BPFLogLevel
    23  	VerifierLogSize  int
    24  }
    25  
    26  func (p *ProgramSocketFilter) Load(opts ProgSKFilterLoadOpts) (log string, err error) {
    27  	return p.load(bpfsys.BPFAttrProgramLoad{
    28  		LogLevel: opts.VerifierLogLevel,
    29  		LogSize:  uint32(opts.VerifierLogSize),
    30  	})
    31  }
    32  
    33  // ErrProgramNotSocketFilterType is returned when attempting to attach a non-socket filter program to a socket.
    34  var ErrProgramNotSocketFilterType = errors.New("the program is not loaded as an socket filter program and " +
    35  	"thus can't be attached as such")
    36  
    37  // SocketAttachControlFunc attaches a "socket filter" program to a network socket. This function is meant to be used
    38  // as function pointer in net.Dialer.Control or net.ListenConfig.Control.
    39  func (p *ProgramSocketFilter) SocketAttachControlFunc(network, address string, c syscall.RawConn) error {
    40  	var err error
    41  	cerr := c.Control(func(fd uintptr) {
    42  		err = p.Attach(fd)
    43  	})
    44  	if err != nil {
    45  		return fmt.Errorf("socket attach: %w", err)
    46  	}
    47  	if cerr != nil {
    48  		return fmt.Errorf("socket attach: %w", cerr)
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  // Attach attempts to attach a filter program to the network socket indicated by the given file descriptor.
    55  // This function can be used if network file descriptors are managed outside of the net package or when using
    56  // the net.TCPListener.File function to get a duplicate file descriptor.
    57  func (p *ProgramSocketFilter) Attach(fd uintptr) error {
    58  	if !p.loaded {
    59  		return ErrProgramNotLoaded
    60  	}
    61  
    62  	if p.ProgramType != bpftypes.BPF_PROG_TYPE_SOCKET_FILTER {
    63  		return ErrProgramNotSocketFilterType
    64  	}
    65  
    66  	err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_ATTACH_BPF, int(p.fd))
    67  	if err != nil {
    68  		return fmt.Errorf("syscall setsockopt: %w", err)
    69  	}
    70  
    71  	p.AttachedSocketFDs = append(p.AttachedSocketFDs, int(fd))
    72  
    73  	return nil
    74  }
    75  
    76  type ProgSKFilterDetachOpts struct {
    77  	// the file descriptor of the network socket from which the program should be detached
    78  	Fd int
    79  	// If true, the program will be detached from all network interfaces
    80  	All bool
    81  }
    82  
    83  // Detach detaches the program from one or all sockets.
    84  func (p *ProgramSocketFilter) Detach(settings ProgSKFilterDetachOpts) error {
    85  	if settings.All {
    86  		for _, fd := range p.AttachedSocketFDs {
    87  			err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_DETACH_BPF, int(p.fd))
    88  			if err != nil {
    89  				return fmt.Errorf("syscall setsockopt: %w", err)
    90  			}
    91  		}
    92  
    93  		// Clear attached socket fd's slice
    94  		p.AttachedSocketFDs = nil
    95  		return nil
    96  	}
    97  
    98  	err := syscall.SetsockoptInt(settings.Fd, syscall.SOL_SOCKET, unix.SO_DETACH_BPF, int(p.fd))
    99  	if err != nil {
   100  		return fmt.Errorf("syscall setsockopt: %w", err)
   101  	}
   102  
   103  	// Delete the FD from the list of attached socket fd's
   104  	for i, fd := range p.AttachedSocketFDs {
   105  		if fd == settings.Fd {
   106  			p.AttachedSocketFDs[i] = p.AttachedSocketFDs[len(p.AttachedSocketFDs)-1]
   107  			p.AttachedSocketFDs = p.AttachedSocketFDs[:len(p.AttachedSocketFDs)-1]
   108  			break
   109  		}
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // Unpin captures the file descriptor of the program at the given 'relativePath' from the kernel.
   116  // If 'deletePin' is true the bpf FS pin will be removed after successfully loading the program, thus transferring
   117  // ownership of the program in a scenario where the program is not shared between multiple userspace programs.
   118  // Otherwise the pin will keep existing which will cause the map to not be deleted when this program exits.
   119  func (p *ProgramSocketFilter) Unpin(relativePath string, deletePin bool) error {
   120  	return p.unpin(relativePath, deletePin)
   121  }