128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
// 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.
|
|
|
|
//go:build linux
|
|
|
|
// Generate a local routing table structure following the code at
|
|
// https://github.com/google/gopacket/blob/master/routing/routing.go
|
|
|
|
package netroute
|
|
|
|
import (
|
|
"net"
|
|
"sort"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"github.com/google/gopacket/routing"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
func New() (routing.Router, error) {
|
|
rtr := &router{}
|
|
rtr.ifaces = make(map[int]net.Interface)
|
|
rtr.addrs = make(map[int]ipAddrs)
|
|
tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
msgs, err := syscall.ParseNetlinkMessage(tab)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
loop:
|
|
for _, m := range msgs {
|
|
switch m.Header.Type {
|
|
case syscall.NLMSG_DONE:
|
|
break loop
|
|
case syscall.RTM_NEWROUTE:
|
|
rt := (*routeInfoInMemory)(unsafe.Pointer(&m.Data[0]))
|
|
routeInfo := rtInfo{}
|
|
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch rt.Family {
|
|
case syscall.AF_INET:
|
|
rtr.v4 = append(rtr.v4, &routeInfo)
|
|
case syscall.AF_INET6:
|
|
rtr.v6 = append(rtr.v6, &routeInfo)
|
|
default:
|
|
continue loop
|
|
}
|
|
for _, attr := range attrs {
|
|
switch attr.Attr.Type {
|
|
case syscall.RTA_DST:
|
|
routeInfo.Dst = &net.IPNet{
|
|
IP: net.IP(attr.Value),
|
|
Mask: net.CIDRMask(int(rt.DstLen), len(attr.Value)*8),
|
|
}
|
|
case syscall.RTA_SRC:
|
|
routeInfo.Src = &net.IPNet{
|
|
IP: net.IP(attr.Value),
|
|
Mask: net.CIDRMask(int(rt.SrcLen), len(attr.Value)*8),
|
|
}
|
|
case syscall.RTA_GATEWAY:
|
|
routeInfo.Gateway = net.IP(attr.Value)
|
|
case syscall.RTA_PREFSRC:
|
|
routeInfo.PrefSrc = net.IP(attr.Value)
|
|
case syscall.RTA_IIF:
|
|
routeInfo.InputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0]))
|
|
case syscall.RTA_OIF:
|
|
routeInfo.OutputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0]))
|
|
case syscall.RTA_PRIORITY:
|
|
routeInfo.Priority = *(*uint32)(unsafe.Pointer(&attr.Value[0]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sort.Sort(rtr.v4)
|
|
sort.Sort(rtr.v6)
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, iface := range ifaces {
|
|
rtr.ifaces[iface.Index] = iface
|
|
var addrs ipAddrs
|
|
ifaceAddrs, err := iface.Addrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, addr := range ifaceAddrs {
|
|
if inet, ok := addr.(*net.IPNet); ok {
|
|
// 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 v4 := inet.IP.To4(); v4 != nil {
|
|
if addrs.v4 == nil {
|
|
addrs.v4 = v4
|
|
}
|
|
} else if addrs.v6 == nil {
|
|
addrs.v6 = inet.IP
|
|
}
|
|
}
|
|
}
|
|
rtr.addrs[iface.Index] = addrs
|
|
}
|
|
return rtr, nil
|
|
}
|