File: route.go

package info (click to toggle)
golang-github-jsimonetti-rtnetlink 2.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,264 kB
  • sloc: makefile: 2
file content (164 lines) | stat: -rw-r--r-- 3,743 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
}