Skip to content

Commit 7439078

Browse files
types: add gws field to Route for ECMP support
Add a GWs []net.IP field to the Route struct to enable ECMP (Equal-Cost Multi-Path) routing via multiple next hops. When both GW and GWs are set, GW is treated as an additional next hop appended after GWs. The NextHops() method returns the combined list for use by plugin implementations. Also document the gws field and its merge behavior in SPEC.md. Signed-off-by: Yusho Yamaguchi <ys-yamaguchi@kddi.com>
1 parent 7c27007 commit 7439078

3 files changed

Lines changed: 33 additions & 9 deletions

File tree

SPEC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ Plugins must output a JSON object with the following keys upon a successful `ADD
585585
- `routes`: Routes created by this attachment:
586586
- `dst`: The destination of the route, in CIDR notation
587587
- `gw`: The next hop address. If unset, a value in `gateway` in the `ips` array may be used.
588+
- `gws` : A list of next hop addresses for ECMP (Equal-Cost Multi-Path) routing. Each entry is an IP address string.
588589
- `mtu` (uint): The MTU (Maximum transmission unit) along the path to the destination.
589590
- `advmss` (uint): The MSS (Maximal Segment Size) to advertise to these destinations when establishing TCP connections.
590591
- `priority` (uint): The priority of route, lower is higher.

pkg/types/types.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ func (d *DNS) Copy() *DNS {
180180
type Route struct {
181181
Dst net.IPNet
182182
GW net.IP
183+
GWs []net.IP
183184
MTU int
184185
AdvMSS int
185186
Priority int
@@ -198,7 +199,7 @@ func (r *Route) String() string {
198199
scope = fmt.Sprintf("%d", *r.Scope)
199200
}
200201

201-
return fmt.Sprintf("{Dst:%+v GW:%v MTU:%d AdvMSS:%d Priority:%d Table:%s Scope:%s}", r.Dst, r.GW, r.MTU, r.AdvMSS, r.Priority, table, scope)
202+
return fmt.Sprintf("{Dst:%+v GW:%v GWs:%v MTU:%d AdvMSS:%d Priority:%d Table:%s Scope:%s}", r.Dst, r.GW, r.GWs, r.MTU, r.AdvMSS, r.Priority, table, scope)
202203
}
203204

204205
func (r *Route) Copy() *Route {
@@ -215,6 +216,11 @@ func (r *Route) Copy() *Route {
215216
Scope: r.Scope,
216217
}
217218

219+
if len(r.GWs) > 0 {
220+
route.GWs = make([]net.IP, len(r.GWs))
221+
copy(route.GWs, r.GWs)
222+
}
223+
218224
if r.Table != nil {
219225
table := *r.Table
220226
route.Table = &table
@@ -277,13 +283,14 @@ func (e *Error) Print() error {
277283

278284
// JSON (un)marshallable types
279285
type route struct {
280-
Dst IPNet `json:"dst"`
281-
GW net.IP `json:"gw,omitempty"`
282-
MTU int `json:"mtu,omitempty"`
283-
AdvMSS int `json:"advmss,omitempty"`
284-
Priority int `json:"priority,omitempty"`
285-
Table *int `json:"table,omitempty"`
286-
Scope *int `json:"scope,omitempty"`
286+
Dst IPNet `json:"dst"`
287+
GW net.IP `json:"gw,omitempty"`
288+
GWs []net.IP `json:"gws,omitempty"`
289+
MTU int `json:"mtu,omitempty"`
290+
AdvMSS int `json:"advmss,omitempty"`
291+
Priority int `json:"priority,omitempty"`
292+
Table *int `json:"table,omitempty"`
293+
Scope *int `json:"scope,omitempty"`
287294
}
288295

289296
func (r *Route) UnmarshalJSON(data []byte) error {
@@ -294,6 +301,7 @@ func (r *Route) UnmarshalJSON(data []byte) error {
294301

295302
r.Dst = net.IPNet(rt.Dst)
296303
r.GW = rt.GW
304+
r.GWs = rt.GWs
297305
r.MTU = rt.MTU
298306
r.AdvMSS = rt.AdvMSS
299307
r.Priority = rt.Priority
@@ -307,6 +315,7 @@ func (r Route) MarshalJSON() ([]byte, error) {
307315
rt := route{
308316
Dst: IPNet(r.Dst),
309317
GW: r.GW,
318+
GWs: r.GWs,
310319
MTU: r.MTU,
311320
AdvMSS: r.AdvMSS,
312321
Priority: r.Priority,

pkg/types/types_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ var _ = Describe("Types", func() {
107107
Expect(unmarshaled).To(Equal(example))
108108
})
109109

110+
It("marshals and unmarshals gws to JSON", func() {
111+
example.GWs = []net.IP{net.ParseIP("1.2.3.2"), net.ParseIP("1.2.3.3")}
112+
jsonBytes, err := json.Marshal(example)
113+
Expect(err).NotTo(HaveOccurred())
114+
Expect(jsonBytes).To(MatchJSON(`{ "dst": "1.2.3.0/24", "gw": "1.2.3.1", "gws": ["1.2.3.2", "1.2.3.3"], "mtu": 1500, "advmss": 1340, "priority": 100, "table": 50, "scope": 253 }`))
115+
116+
var unmarshaled types.Route
117+
Expect(json.Unmarshal(jsonBytes, &unmarshaled)).To(Succeed())
118+
Expect(unmarshaled.GWs).To(HaveLen(2))
119+
Expect(unmarshaled.GWs[0].String()).To(Equal("1.2.3.2"))
120+
Expect(unmarshaled.GWs[1].String()).To(Equal("1.2.3.3"))
121+
})
122+
110123
Context("when the json data is not valid", func() {
111124
Specify("UnmarshalJSON returns an error", func() {
112125
route := new(types.Route)
@@ -116,8 +129,9 @@ var _ = Describe("Types", func() {
116129
})
117130

118131
It("formats as a string with a hex mask", func() {
119-
Expect(example.String()).To(Equal(`{Dst:{IP:1.2.3.0 Mask:ffffff00} GW:1.2.3.1 MTU:1500 AdvMSS:1340 Priority:100 Table:50 Scope:253}`))
132+
Expect(example.String()).To(Equal(`{Dst:{IP:1.2.3.0 Mask:ffffff00} GW:1.2.3.1 GWs:[] MTU:1500 AdvMSS:1340 Priority:100 Table:50 Scope:253}`))
120133
})
134+
121135
})
122136

123137
Describe("Error type", func() {

0 commit comments

Comments
 (0)