// 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.

// +build linux

// Package routing provides a very basic but mostly functional implementation of // a routing table for IPv4/IPv6 addresses. It uses a routing table pulled from // the kernel via netlink to find the correct interface, gateway, and preferred // source IP address for packets destined to a particular location. // // The routing package is meant to be used with applications that are sending // raw packet data, which don't have the benefit of having the kernel route // packets for them.
package routing import ( ) // Pulled from http://man7.org/linux/man-pages/man7/rtnetlink.7.html // See the section on RTM_NEWROUTE, specifically 'struct rtmsg'. type routeInfoInMemory struct { Family byte DstLen byte SrcLen byte TOS byte Table byte Protocol byte Scope byte Type byte Flags uint32 } // 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 } // 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 []net.Interface addrs []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") } if != nil { return } // Interfaces are 1-indexed, but we store them in a 0-indexed array. -- = &.ifaces[] 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 } } } for , := range { if .InputIface != 0 && .InputIface != { continue } if .Src != nil && !.Src.Contains() { continue } if .Dst != nil && !.Dst.Contains() { continue } return int(.OutputIface), .Gateway, .PrefSrc, nil } = fmt.Errorf("no route found for %v", ) return } // New creates a new router object. The router returned by New currently does // not update its routes after construction... care should be taken for // long-running programs to call New() regularly to take into account any // changes to the routing table which have occurred since the last New() call. func () (Router, error) { := &router{} , := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) if != nil { return nil, } , := syscall.ParseNetlinkMessage() if != nil { return nil, } : for , := range { switch .Header.Type { case syscall.NLMSG_DONE: break case syscall.RTM_NEWROUTE: := (*routeInfoInMemory)(unsafe.Pointer(&.Data[0])) := rtInfo{} , := syscall.ParseNetlinkRouteAttr(&) if != nil { return nil, } switch .Family { case syscall.AF_INET: .v4 = append(.v4, &) case syscall.AF_INET6: .v6 = append(.v6, &) default: continue } for , := range { switch .Attr.Type { case syscall.RTA_DST: .Dst = &net.IPNet{ IP: net.IP(.Value), Mask: net.CIDRMask(int(.DstLen), len(.Value)*8), } case syscall.RTA_SRC: .Src = &net.IPNet{ IP: net.IP(.Value), Mask: net.CIDRMask(int(.SrcLen), len(.Value)*8), } case syscall.RTA_GATEWAY: .Gateway = net.IP(.Value) case syscall.RTA_PREFSRC: .PrefSrc = net.IP(.Value) case syscall.RTA_IIF: .InputIface = *(*uint32)(unsafe.Pointer(&.Value[0])) case syscall.RTA_OIF: .OutputIface = *(*uint32)(unsafe.Pointer(&.Value[0])) case syscall.RTA_PRIORITY: .Priority = *(*uint32)(unsafe.Pointer(&.Value[0])) } } } } sort.Sort(.v4) sort.Sort(.v6) , := net.Interfaces() if != nil { return nil, } for , := range { if != .Index-1 { return nil, fmt.Errorf("out of order iface %d = %v", , ) } .ifaces = append(.ifaces, ) var ipAddrs , := .Addrs() if != nil { return nil, } for , := range { if , := .(*net.IPNet); { // Go has a nasty habit of giving you IPv4s as ::ffff:1.2.3.4 instead of 1.2.3.4. // We want to use mapped v4 addresses as v4 preferred addresses, never as v6 // preferred addresses. if := .IP.To4(); != nil { if .v4 == nil { .v4 = } } else if .v6 == nil { .v6 = .IP } } } .addrs = append(.addrs, ) } return , nil }