//go:build linux package instanceexec import ( "errors" "net" "syscall" "time" "golang.org/x/sys/unix" ) // accessWritable asks the kernel whether path could be opened for writing // without opening it: on a read-only mount access(2) answers EROFS, which // is exactly the property ProtectControlGroups= must give a tenant. func accessWritable(path string) error { return unix.Access(path, unix.W_OK) } // sendOneDatagram sends one empty UDP datagram. With a cgroup BPF egress // filter (IPAddressDeny=) the filter's verdict is sendmsg's return value, // EPERM; with no route it is ENETUNREACH; on a live path it is nil. func sendOneDatagram(network, addr string) error { c, err := net.Dial(network, addr) if err != nil { return unwrapErrno(err) } defer c.Close() _, err = c.Write([]byte{}) return unwrapErrno(err) } func connectWithTimeout(addr string, timeout time.Duration) error { c, err := net.DialTimeout("tcp", addr, timeout) if err != nil { return unwrapErrno(err) } c.Close() return nil } // unwrapErrno returns the syscall.Errno inside err so callers can // errors.Is against EPERM and ENETUNREACH; net.OpError and os.SyscallError // both unwrap to the Errno, so errors.As finds it. Anything else is // returned as is. (unix.Errno is an alias of syscall.Errno.) func unwrapErrno(err error) error { var errno syscall.Errno if errors.As(err, &errno) { return errno } return err }