1package netlink
2
3import (
4	"fmt"
5	"net"
6	"strings"
7)
8
9// Scope is an enum representing a route scope.
10type Scope uint8
11
12type NextHopFlag int
13
14type Destination interface {
15	Family() int
16	Decode([]byte) error
17	Encode() ([]byte, error)
18	String() string
19	Equal(Destination) bool
20}
21
22type Encap interface {
23	Type() int
24	Decode([]byte) error
25	Encode() ([]byte, error)
26	String() string
27	Equal(Encap) bool
28}
29
30// Route represents a netlink route.
31type Route struct {
32	LinkIndex  int
33	ILinkIndex int
34	Scope      Scope
35	Dst        *net.IPNet
36	Src        net.IP
37	Gw         net.IP
38	MultiPath  []*NexthopInfo
39	Protocol   int
40	Priority   int
41	Table      int
42	Type       int
43	Tos        int
44	Flags      int
45	MPLSDst    *int
46	NewDst     Destination
47	Encap      Encap
48	MTU        int
49	AdvMSS     int
50	Hoplimit   int
51}
52
53func (r Route) String() string {
54	elems := []string{}
55	if len(r.MultiPath) == 0 {
56		elems = append(elems, fmt.Sprintf("Ifindex: %d", r.LinkIndex))
57	}
58	if r.MPLSDst != nil {
59		elems = append(elems, fmt.Sprintf("Dst: %d", r.MPLSDst))
60	} else {
61		elems = append(elems, fmt.Sprintf("Dst: %s", r.Dst))
62	}
63	if r.NewDst != nil {
64		elems = append(elems, fmt.Sprintf("NewDst: %s", r.NewDst))
65	}
66	if r.Encap != nil {
67		elems = append(elems, fmt.Sprintf("Encap: %s", r.Encap))
68	}
69	elems = append(elems, fmt.Sprintf("Src: %s", r.Src))
70	if len(r.MultiPath) > 0 {
71		elems = append(elems, fmt.Sprintf("Gw: %s", r.MultiPath))
72	} else {
73		elems = append(elems, fmt.Sprintf("Gw: %s", r.Gw))
74	}
75	elems = append(elems, fmt.Sprintf("Flags: %s", r.ListFlags()))
76	elems = append(elems, fmt.Sprintf("Table: %d", r.Table))
77	return fmt.Sprintf("{%s}", strings.Join(elems, " "))
78}
79
80func (r Route) Equal(x Route) bool {
81	return r.LinkIndex == x.LinkIndex &&
82		r.ILinkIndex == x.ILinkIndex &&
83		r.Scope == x.Scope &&
84		ipNetEqual(r.Dst, x.Dst) &&
85		r.Src.Equal(x.Src) &&
86		r.Gw.Equal(x.Gw) &&
87		nexthopInfoSlice(r.MultiPath).Equal(x.MultiPath) &&
88		r.Protocol == x.Protocol &&
89		r.Priority == x.Priority &&
90		r.Table == x.Table &&
91		r.Type == x.Type &&
92		r.Tos == x.Tos &&
93		r.Hoplimit == x.Hoplimit &&
94		r.Flags == x.Flags &&
95		(r.MPLSDst == x.MPLSDst || (r.MPLSDst != nil && x.MPLSDst != nil && *r.MPLSDst == *x.MPLSDst)) &&
96		(r.NewDst == x.NewDst || (r.NewDst != nil && r.NewDst.Equal(x.NewDst))) &&
97		(r.Encap == x.Encap || (r.Encap != nil && r.Encap.Equal(x.Encap)))
98}
99
100func (r *Route) SetFlag(flag NextHopFlag) {
101	r.Flags |= int(flag)
102}
103
104func (r *Route) ClearFlag(flag NextHopFlag) {
105	r.Flags &^= int(flag)
106}
107
108type flagString struct {
109	f NextHopFlag
110	s string
111}
112
113// RouteUpdate is sent when a route changes - type is RTM_NEWROUTE or RTM_DELROUTE
114type RouteUpdate struct {
115	Type uint16
116	Route
117}
118
119type NexthopInfo struct {
120	LinkIndex int
121	Hops      int
122	Gw        net.IP
123	Flags     int
124	NewDst    Destination
125	Encap     Encap
126}
127
128func (n *NexthopInfo) String() string {
129	elems := []string{}
130	elems = append(elems, fmt.Sprintf("Ifindex: %d", n.LinkIndex))
131	if n.NewDst != nil {
132		elems = append(elems, fmt.Sprintf("NewDst: %s", n.NewDst))
133	}
134	if n.Encap != nil {
135		elems = append(elems, fmt.Sprintf("Encap: %s", n.Encap))
136	}
137	elems = append(elems, fmt.Sprintf("Weight: %d", n.Hops+1))
138	elems = append(elems, fmt.Sprintf("Gw: %s", n.Gw))
139	elems = append(elems, fmt.Sprintf("Flags: %s", n.ListFlags()))
140	return fmt.Sprintf("{%s}", strings.Join(elems, " "))
141}
142
143func (n NexthopInfo) Equal(x NexthopInfo) bool {
144	return n.LinkIndex == x.LinkIndex &&
145		n.Hops == x.Hops &&
146		n.Gw.Equal(x.Gw) &&
147		n.Flags == x.Flags &&
148		(n.NewDst == x.NewDst || (n.NewDst != nil && n.NewDst.Equal(x.NewDst))) &&
149		(n.Encap == x.Encap || (n.Encap != nil && n.Encap.Equal(x.Encap)))
150}
151
152type nexthopInfoSlice []*NexthopInfo
153
154func (n nexthopInfoSlice) Equal(x []*NexthopInfo) bool {
155	if len(n) != len(x) {
156		return false
157	}
158	for i := range n {
159		if n[i] == nil || x[i] == nil {
160			return false
161		}
162		if !n[i].Equal(*x[i]) {
163			return false
164		}
165	}
166	return true
167}
168
169// ipNetEqual returns true iff both IPNet are equal
170func ipNetEqual(ipn1 *net.IPNet, ipn2 *net.IPNet) bool {
171	if ipn1 == ipn2 {
172		return true
173	}
174	if ipn1 == nil || ipn2 == nil {
175		return false
176	}
177	m1, _ := ipn1.Mask.Size()
178	m2, _ := ipn2.Mask.Size()
179	return m1 == m2 && ipn1.IP.Equal(ipn2.IP)
180}
181