primeiro commit
This commit is contained in:
151
internal/probe/route_linux.go
Normal file
151
internal/probe/route_linux.go
Normal file
@@ -0,0 +1,151 @@
|
||||
//go:build linux
|
||||
|
||||
package probe
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const routePath = "/proc/net/route"
|
||||
|
||||
const (
|
||||
routeFlagUp = 0x1
|
||||
routeFlagGateway = 0x2
|
||||
routeFlagHost = 0x4
|
||||
)
|
||||
|
||||
type linuxRoute struct {
|
||||
iface string
|
||||
destination net.IP
|
||||
gateway net.IP
|
||||
flags int64
|
||||
mask net.IP
|
||||
}
|
||||
|
||||
// GatewayForInterface returns the default IPv4 gateway for an interface.
|
||||
func GatewayForInterface(ctx context.Context, iface string) (net.IP, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, fmt.Errorf("checking context before reading routes: %w", err)
|
||||
}
|
||||
data, err := os.ReadFile(routePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %w", routePath, err)
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, fmt.Errorf("checking context after reading routes: %w", err)
|
||||
}
|
||||
ip, err := parseLinuxDefaultGateway(data, iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func parseLinuxDefaultGateway(data []byte, iface string) (net.IP, error) {
|
||||
routes, err := parseLinuxRoutes(data, iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasDefaultRoute := false
|
||||
for _, route := range routes {
|
||||
if !route.isDefault() {
|
||||
continue
|
||||
}
|
||||
hasDefaultRoute = true
|
||||
if route.hasFlag(routeFlagUp) && route.hasFlag(routeFlagGateway) && !route.gateway.Equal(net.IPv4zero) {
|
||||
return route.gateway, nil
|
||||
}
|
||||
}
|
||||
|
||||
if hasDefaultRoute {
|
||||
for _, route := range routes {
|
||||
if route.isPointToPointPeer() {
|
||||
return route.destination, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("default gateway not found")
|
||||
}
|
||||
|
||||
func parseLinuxRoutes(data []byte, iface string) ([]linuxRoute, error) {
|
||||
var routes []linuxRoute
|
||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < 8 || fields[0] != iface {
|
||||
continue
|
||||
}
|
||||
route, err := parseLinuxRoute(fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routes = append(routes, route)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("scanning %s contents: %w", routePath, err)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func parseLinuxRoute(fields []string) (linuxRoute, error) {
|
||||
destination, err := littleEndianHexIPv4(fields[1])
|
||||
if err != nil {
|
||||
return linuxRoute{}, fmt.Errorf("parsing route destination for %s: %w", fields[0], err)
|
||||
}
|
||||
gateway, err := littleEndianHexIPv4(fields[2])
|
||||
if err != nil {
|
||||
return linuxRoute{}, fmt.Errorf("parsing route gateway for %s: %w", fields[0], err)
|
||||
}
|
||||
flags, err := strconv.ParseInt(fields[3], 16, 64)
|
||||
if err != nil {
|
||||
return linuxRoute{}, fmt.Errorf("parsing route flags for %s: %w", fields[0], err)
|
||||
}
|
||||
mask, err := littleEndianHexIPv4(fields[7])
|
||||
if err != nil {
|
||||
return linuxRoute{}, fmt.Errorf("parsing route mask for %s: %w", fields[0], err)
|
||||
}
|
||||
return linuxRoute{
|
||||
iface: fields[0],
|
||||
destination: destination,
|
||||
gateway: gateway,
|
||||
flags: flags,
|
||||
mask: mask,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r linuxRoute) isDefault() bool {
|
||||
return r.destination.Equal(net.IPv4zero) && r.mask.Equal(net.IPv4zero)
|
||||
}
|
||||
|
||||
func (r linuxRoute) isPointToPointPeer() bool {
|
||||
return r.hasFlag(routeFlagUp) &&
|
||||
r.hasFlag(routeFlagHost) &&
|
||||
r.gateway.Equal(net.IPv4zero) &&
|
||||
!r.destination.Equal(net.IPv4zero)
|
||||
}
|
||||
|
||||
func (r linuxRoute) hasFlag(flag int64) bool {
|
||||
return r.flags&flag != 0
|
||||
}
|
||||
|
||||
func littleEndianHexIPv4(value string) (net.IP, error) {
|
||||
decoded, err := hex.DecodeString(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decoded) != net.IPv4len {
|
||||
return nil, fmt.Errorf("expected 4 bytes, got %d", len(decoded))
|
||||
}
|
||||
return net.IPv4(decoded[3], decoded[2], decoded[1], decoded[0]), nil
|
||||
}
|
||||
Reference in New Issue
Block a user