mirror of
https://github.com/jsimonetti/rtnetlink.git
synced 2026-03-29 08:51:53 +02:00
In order to update to v2.0.0, we need to update the module path per the Go module documentation. * https://go.dev/doc/modules/major-version Fixes: https://github.com/jsimonetti/rtnetlink/issues/225 This can be released as v2.0.1 Signed-off-by: SuperQ <superq@gmail.com>
165 lines
3.7 KiB
Go
165 lines
3.7 KiB
Go
package rtnl
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
|
|
|
|
"github.com/jsimonetti/rtnetlink/v2"
|
|
)
|
|
|
|
// Route represents a route table entry
|
|
type Route struct {
|
|
Destination *net.IPNet
|
|
Gateway net.IP
|
|
Interface *net.Interface
|
|
Metric uint32
|
|
}
|
|
|
|
// generating route message
|
|
func genRouteMessage(ifc *net.Interface, dst net.IPNet, gw net.IP, options ...RouteOption) (rm *rtnetlink.RouteMessage, err error) {
|
|
opts := DefaultRouteOptions(ifc, dst, gw)
|
|
|
|
for _, option := range options {
|
|
option(opts)
|
|
}
|
|
|
|
af, err := addrFamily(dst.IP)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Determine scope
|
|
var scope uint8
|
|
switch {
|
|
case gw != nil:
|
|
scope = unix.RT_SCOPE_UNIVERSE
|
|
case len(dst.IP) == net.IPv6len && dst.IP.To4() == nil:
|
|
scope = unix.RT_SCOPE_UNIVERSE
|
|
default:
|
|
// Set default scope to LINK
|
|
scope = unix.RT_SCOPE_LINK
|
|
}
|
|
|
|
var srclen int
|
|
if opts.Src != nil {
|
|
srclen, _ = opts.Src.Mask.Size()
|
|
opts.Attrs.Src = opts.Src.IP
|
|
}
|
|
|
|
dstlen, _ := dst.Mask.Size()
|
|
|
|
tx := &rtnetlink.RouteMessage{
|
|
Family: uint8(af),
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Protocol: unix.RTPROT_BOOT,
|
|
Type: unix.RTN_UNICAST,
|
|
Scope: scope,
|
|
DstLength: uint8(dstlen),
|
|
SrcLength: uint8(srclen),
|
|
Attributes: opts.Attrs,
|
|
}
|
|
return tx, nil
|
|
}
|
|
|
|
// RouteAdd adds information about a network route.
|
|
func (c *Conn) RouteAdd(ifc *net.Interface, dst net.IPNet, gw net.IP, options ...RouteOption) (err error) {
|
|
rm, err := genRouteMessage(ifc, dst, gw, options...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.Conn.Route.Add(rm)
|
|
}
|
|
|
|
// RouteReplace adds or replace information about a network route.
|
|
func (c *Conn) RouteReplace(ifc *net.Interface, dst net.IPNet, gw net.IP, options ...RouteOption) (err error) {
|
|
rm, err := genRouteMessage(ifc, dst, gw, options...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.Conn.Route.Replace(rm)
|
|
}
|
|
|
|
// RouteDel deletes the route to the given destination.
|
|
func (c *Conn) RouteDel(ifc *net.Interface, dst net.IPNet) error {
|
|
af, err := addrFamily(dst.IP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prefixlen, _ := dst.Mask.Size()
|
|
attr := rtnetlink.RouteAttributes{
|
|
Dst: dst.IP,
|
|
OutIface: uint32(ifc.Index),
|
|
}
|
|
tx := &rtnetlink.RouteMessage{
|
|
Family: uint8(af),
|
|
Table: unix.RT_TABLE_MAIN,
|
|
DstLength: uint8(prefixlen),
|
|
Attributes: attr,
|
|
}
|
|
return c.Conn.Route.Delete(tx)
|
|
}
|
|
|
|
// RouteGet gets a single route to the given destination address.
|
|
func (c *Conn) RouteGet(dst net.IP) (*Route, error) {
|
|
list, err := c.RouteGetAll(dst)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(list) == 0 {
|
|
return nil, errors.New("route wrong length")
|
|
}
|
|
|
|
return list[0], nil
|
|
}
|
|
|
|
// RouteGetAll returns all routes to the given destination IP in the main routing table.
|
|
func (c *Conn) RouteGetAll(dst net.IP) (ret []*Route, err error) {
|
|
af, err := addrFamily(dst)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attr := rtnetlink.RouteAttributes{
|
|
Dst: dst,
|
|
}
|
|
|
|
tx := &rtnetlink.RouteMessage{
|
|
Family: uint8(af),
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Attributes: attr,
|
|
}
|
|
|
|
rx, err := c.Conn.Route.Get(tx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, rt := range rx {
|
|
ifindex := int(rt.Attributes.OutIface)
|
|
|
|
iface, err := c.LinkByIndex(ifindex)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get link by interface index: %w", err)
|
|
}
|
|
|
|
_, dstNet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", rt.Attributes.Dst.String(), rt.DstLength))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to construct CIDR from route destination address and length: %w", err)
|
|
}
|
|
|
|
ret = append(ret, &Route{
|
|
Destination: dstNet,
|
|
Gateway: rt.Attributes.Gateway,
|
|
Interface: iface,
|
|
Metric: rt.Attributes.Priority,
|
|
})
|
|
}
|
|
|
|
return ret, nil
|
|
}
|