blob: 6742a67692e2bd9045a1f6a54de9e5a8c475a4ee [file] [edit]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package directaccess
import (
"fmt"
"net"
btopt "cloud.google.com/go/bigtable/internal/option"
)
// CheckLoopbackInterfaceUp verifies that at least one loopback interface is UP.
func CheckLoopbackInterfaceUp() error {
btopt.Debugf(nil, "directaccess: Checking for UP loopback interfaces")
ifaces, err := net.Interfaces()
if err != nil {
return fmt.Errorf("failed to list network interfaces: %w", err)
}
for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 && iface.Flags&net.FlagUp != 0 {
return nil
}
}
return fmt.Errorf("no loopback interface found in UP state")
}
func skipLoopback(iface net.Interface) error {
if iface.Flags&net.FlagLoopback != 0 {
return fmt.Errorf("is loopback")
}
if iface.Flags&net.FlagUp != net.FlagUp {
return fmt.Errorf("not up")
}
return nil
}
func onlyLoopback(iface net.Interface) error {
if iface.Flags&net.FlagLoopback == 0 {
return fmt.Errorf("not loopback")
}
if iface.Flags&net.FlagUp != net.FlagUp {
return fmt.Errorf("not up")
}
return nil
}
// CheckLocalIPv6Addresses searches the host's non-loopback network interfaces
// to verify if the specified IPv6 address is currently plumbed and active.
// It returns the interface where the address is configured, or an error if not
func CheckLocalIPv6Addresses(ip *net.IP) (*net.Interface, error) {
btopt.Debugf(nil, "directaccess: Searching for local IPv6 address: %s", ip.String())
return findLocalAddress(func(i net.IP) bool { return i.To4() == nil && i.Equal(*ip) }, skipLoopback)
}
// CheckLocalIPv4Addresses searches the host's non-loopback network interfaces
// to verify if the specified IPv4 address is currently plumbed and active.
// It returns the interface where the address is configured, or an error if not found.
func CheckLocalIPv4Addresses(ip *net.IP) (*net.Interface, error) {
btopt.Debugf(nil, "directaccess: Searching for local IPv4 address: %s", ip.String())
return findLocalAddress(func(i net.IP) bool { return i.To4() != nil && i.Equal(*ip) }, skipLoopback)
}
// CheckLocalIPv6LoopbackAddress verifies that the standard IPv6 loopback
// address (::1) is configured and assigned to a local loopback interface.
func CheckLocalIPv6LoopbackAddress() error {
btopt.Debugf(nil, "directaccess: Searching for IPv6 loopback (::1)")
_, err := findLocalAddress(func(i net.IP) bool { return i.Equal(net.ParseIP("::1")) }, onlyLoopback)
return err
}
// CheckLocalIPv4LoopbackAddress verifies that the standard IPv4 loopback
// address (127.0.0.1) is configured and assigned to a local loopback interface.
func CheckLocalIPv4LoopbackAddress() error {
btopt.Debugf(nil, "directaccess: Searching for IPv4 loopback (127.0.0.1)")
_, err := findLocalAddress(func(i net.IP) bool { return i.Equal(net.ParseIP("127.0.0.1")) }, onlyLoopback)
return err
}
// findLocalAddress is a generic helper that iterates through all local network interfaces.
// It applies the ifaceFilter to skip unwanted interfaces (e.g., ignoring loopbacks
// or inactive NICs) and checks if any assigned IP satisfies the ipMatches condition.
// It returns the first matching network interface, or an error if exhausted.
func findLocalAddress(ipMatches func(net.IP) bool, ifaceFilter func(net.Interface) error) (*net.Interface, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
if err := ifaceFilter(iface); err != nil {
continue
}
ifaddrs, err := iface.Addrs()
if err != nil {
continue
}
for _, ifaddr := range ifaddrs {
if ip, ok := ifaddr.(*net.IPNet); ok && ipMatches(ip.IP) {
return &iface, nil
}
}
}
btopt.Debugf(nil, "directaccess: Target address not found on any valid interface")
return nil, fmt.Errorf("address not found on any interface")
}