// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

// Originally found in // https://github.com/google/gopacket/blob/master/routing/routing.go // - Route selection modified to choose most selective route // to break ties when route priority is insufficient.
package netroute import ( ) // rtInfo contains information on a single route. type rtInfo struct { Src, Dst *net.IPNet Gateway, PrefSrc net.IP // We currently ignore the InputIface. InputIface, OutputIface uint32 Priority uint32 } func ( rtInfo) ( *rtInfo) bool { if == nil { return true } var , int if .Dst != nil { , _ = .Dst.Mask.Size() } if .Dst != nil { , _ = .Dst.Mask.Size() } if > { return true } else if < { return false } // if all else is equal, prefer a route with a gateway. if .Priority == .Priority && .Gateway == nil && .Gateway != nil { return false } // Windows and MacOS hasn't metric/priority on rule entry, // But the interface device has the priority property. // // Before we find more correctly way on different OS platform, // we keep the same rule selecting logical as before which // is more later more special return .Priority >= .Priority } // routeSlice implements sort.Interface to sort routes by Priority. type routeSlice []*rtInfo func ( routeSlice) () int { return len() } func ( routeSlice) (, int) bool { return [].Priority < [].Priority } func ( routeSlice) (, int) { [], [] = [], [] } type router struct { ifaces map[int]net.Interface addrs map[int]ipAddrs v4, v6 routeSlice } func ( *router) () string { := []string{"ROUTER", "--- V4 ---"} for , := range .v4 { = append(, fmt.Sprintf("%+v", *)) } = append(, "--- V6 ---") for , := range .v6 { = append(, fmt.Sprintf("%+v", *)) } return strings.Join(, "\n") } type ipAddrs struct { v4, v6 net.IP } func ( *router) ( net.IP) ( *net.Interface, , net.IP, error) { return .RouteWithSrc(nil, nil, ) } func ( *router) ( net.HardwareAddr, , net.IP) ( *net.Interface, , net.IP, error) { var int switch { case .To4() != nil: , , , = .route(.v4, , , ) case .To16() != nil: , , , = .route(.v6, , , ) default: = errors.New("IP is not valid as IPv4 or IPv6") return } if != nil { return } // Interfaces are 1-indexed, but we store them in a 0-indexed array. , := .ifaces[] if ! { = errors.New("Route refereced unknown interface") } = & if == nil { switch { case .To4() != nil: = .addrs[].v4 case .To16() != nil: = .addrs[].v6 } } return } func ( *router) ( routeSlice, net.HardwareAddr, , net.IP) ( int, , net.IP, error) { var uint32 if != nil { for , := range .ifaces { if bytes.Equal(, .HardwareAddr) { // Convert from zero- to one-indexed. = uint32( + 1) break } } } var *rtInfo for , := range { if .InputIface != 0 && .InputIface != { continue } if != nil && .Src != nil && !.Src.Contains() { continue } if .Dst != nil && !.Dst.Contains() { continue } if .IsMoreSpecThan() { = } } if != nil { return int(.OutputIface), .Gateway, .PrefSrc, nil } = fmt.Errorf("no route found for %v", ) return }