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 }