// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause // Package routetable provides functions that operate on the system's route // table. package routetable import ( "bufio" "fmt" "net/netip" "strconv" "tailscale.com/types/logger" ) var ( defaultRouteIPv4 = RouteDestination{Prefix: netip.PrefixFrom(netip.IPv4Unspecified(), 0)} //lint:ignore U1000 used in routetable_bsd_test.go defaultRouteIPv6 = RouteDestination{Prefix: netip.PrefixFrom(netip.IPv6Unspecified(), 0)} ) // RouteEntry contains common cross-platform fields describing an entry in the // system route table. type RouteEntry struct { // Family is the IP family of the route; it will be either 4 or 6. Family int // Type is the type of this route. Type RouteType // Dst is the destination of the route. Dst RouteDestination // Gatewayis the gateway address specified for this route. // This value will be invalid (where !r.Gateway.IsValid()) in cases // where there is no gateway address for this route. Gateway netip.Addr // Interface is the name of the network interface to use when sending // packets that match this route. This field can be empty. Interface string // Sys contains platform-specific information about this route. Sys any } // Format implements the fmt.Formatter interface. func (r RouteEntry) Format(f fmt.State, verb rune) { logger.ArgWriter(func(w *bufio.Writer) { switch r.Family { case 4: fmt.Fprintf(w, "{Family: IPv4") case 6: fmt.Fprintf(w, "{Family: IPv6") default: fmt.Fprintf(w, "{Family: unknown(%d)", r.Family) } // Match 'ip route' and other tools by not printing the route // type if it's a unicast route. if r.Type != RouteTypeUnicast { fmt.Fprintf(w, ", Type: %s", r.Type) } if r.Dst.IsValid() { fmt.Fprintf(w, ", Dst: %s", r.Dst) } else { w.WriteString(", Dst: invalid") } if r.Gateway.IsValid() { fmt.Fprintf(w, ", Gateway: %s", r.Gateway) } if r.Interface != "" { fmt.Fprintf(w, ", Interface: %s", r.Interface) } if r.Sys != nil { var formatVerb string switch { case f.Flag('#'): formatVerb = "%#v" case f.Flag('+'): formatVerb = "%+v" default: formatVerb = "%v" } fmt.Fprintf(w, ", Sys: "+formatVerb, r.Sys) } w.WriteString("}") }).Format(f, verb) } // RouteDestination is the destination of a route. // // This is similar to net/netip.Prefix, but also contains an optional IPv6 // zone. type RouteDestination struct { netip.Prefix Zone string } func (r RouteDestination) String() string { ip := r.Prefix.Addr() if r.Zone != "" { ip = ip.WithZone(r.Zone) } return ip.String() + "/" + strconv.Itoa(r.Prefix.Bits()) } // RouteType describes the type of a route. type RouteType int const ( // RouteTypeUnspecified is the unspecified route type. RouteTypeUnspecified RouteType = iota // RouteTypeLocal indicates that the destination of this route is an // address that belongs to this system. RouteTypeLocal // RouteTypeUnicast indicates that the destination of this route is a // "regular" address--one that neither belongs to this host, nor is a // broadcast/multicast/etc. address. RouteTypeUnicast // RouteTypeBroadcast indicates that the destination of this route is a // broadcast address. RouteTypeBroadcast // RouteTypeMulticast indicates that the destination of this route is a // multicast address. RouteTypeMulticast // RouteTypeOther indicates that the route is of some other valid type; // see the Sys field for the OS-provided route information to determine // the exact type. RouteTypeOther ) func (r RouteType) String() string { switch r { case RouteTypeUnspecified: return "unspecified" case RouteTypeLocal: return "local" case RouteTypeUnicast: return "unicast" case RouteTypeBroadcast: return "broadcast" case RouteTypeMulticast: return "multicast" case RouteTypeOther: return "other" default: return "invalid" } }