Skip to content

Commit 9e0aabb

Browse files
Merge pull request #19603 from danwinship/auto-egress-ip-3.10-updates
Auto egress IP code reorg in prep for future features
2 parents a2f5c9e + 9fd8ad5 commit 9e0aabb

File tree

2 files changed

+264
-121
lines changed

2 files changed

+264
-121
lines changed

pkg/network/node/egressip.go

Lines changed: 110 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ type nodeEgress struct {
2525
}
2626

2727
type namespaceEgress struct {
28-
vnid uint32
29-
requestedIP string
28+
vnid uint32
29+
requestedIPs []string
3030
}
3131

3232
type egressIPInfo struct {
@@ -37,8 +37,6 @@ type egressIPInfo struct {
3737

3838
assignedNodeIP string
3939
assignedIPTablesMark string
40-
assignedVNID uint32
41-
blockedVNIDs map[uint32]bool
4240
}
4341

4442
type egressIPWatcher struct {
@@ -55,6 +53,9 @@ type egressIPWatcher struct {
5553
namespacesByVNID map[uint32]*namespaceEgress
5654
egressIPs map[string]*egressIPInfo
5755

56+
changedEgressIPs []*egressIPInfo
57+
changedNamespaces []*namespaceEgress
58+
5859
localEgressLink netlink.Link
5960
localEgressNet *net.IPNet
6061

@@ -112,32 +113,57 @@ func (eip *egressIPWatcher) ensureEgressIPInfo(egressIP string) *egressIPInfo {
112113
return eg
113114
}
114115

115-
func (eg *egressIPInfo) addNode(node *nodeEgress) {
116+
func (eip *egressIPWatcher) egressIPChanged(eg *egressIPInfo) {
117+
eip.changedEgressIPs = append(eip.changedEgressIPs, eg)
118+
for _, ns := range eg.namespaces {
119+
eip.changedNamespaces = append(eip.changedNamespaces, ns)
120+
}
121+
}
122+
123+
func (eip *egressIPWatcher) addNode(egressIP string, node *nodeEgress) {
124+
eg := eip.ensureEgressIPInfo(egressIP)
116125
if len(eg.nodes) != 0 {
117126
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", eg.ip, node.nodeIP, eg.nodes[0].nodeIP))
118127
}
119128
eg.nodes = append(eg.nodes, node)
129+
130+
eip.egressIPChanged(eg)
120131
}
121132

122-
func (eg *egressIPInfo) deleteNode(node *nodeEgress) {
133+
func (eip *egressIPWatcher) deleteNode(egressIP string, node *nodeEgress) {
134+
eg := eip.egressIPs[egressIP]
135+
if eg == nil {
136+
return
137+
}
138+
123139
for i := range eg.nodes {
124140
if eg.nodes[i] == node {
141+
eip.egressIPChanged(eg)
125142
eg.nodes = append(eg.nodes[:i], eg.nodes[i+1:]...)
126143
return
127144
}
128145
}
129146
}
130147

131-
func (eg *egressIPInfo) addNamespace(ns *namespaceEgress) {
148+
func (eip *egressIPWatcher) addNamespace(egressIP string, ns *namespaceEgress) {
149+
eg := eip.ensureEgressIPInfo(egressIP)
132150
if len(eg.namespaces) != 0 {
133151
utilruntime.HandleError(fmt.Errorf("Multiple namespaces claiming EgressIP %q (NetIDs %d, %d)", eg.ip, ns.vnid, eg.namespaces[0].vnid))
134152
}
135153
eg.namespaces = append(eg.namespaces, ns)
154+
155+
eip.egressIPChanged(eg)
136156
}
137157

138-
func (eg *egressIPInfo) deleteNamespace(ns *namespaceEgress) {
158+
func (eip *egressIPWatcher) deleteNamespace(egressIP string, ns *namespaceEgress) {
159+
eg := eip.egressIPs[egressIP]
160+
if eg == nil {
161+
return
162+
}
163+
139164
for i := range eg.namespaces {
140165
if eg.namespaces[i] == ns {
166+
eip.egressIPChanged(eg)
141167
eg.namespaces = append(eg.namespaces[:i], eg.namespaces[i+1:]...)
142168
return
143169
}
@@ -183,22 +209,15 @@ func (eip *egressIPWatcher) updateNodeEgress(nodeIP string, nodeEgressIPs []stri
183209
oldRequestedIPs := node.requestedIPs
184210
node.requestedIPs = sets.NewString(nodeEgressIPs...)
185211

186-
// Process new EgressIPs
212+
// Process new and removed EgressIPs
187213
for _, ip := range node.requestedIPs.Difference(oldRequestedIPs).UnsortedList() {
188-
eg := eip.ensureEgressIPInfo(ip)
189-
eg.addNode(node)
190-
eip.syncEgressIP(eg)
214+
eip.addNode(ip, node)
191215
}
192-
193-
// Process removed EgressIPs
194216
for _, ip := range oldRequestedIPs.Difference(node.requestedIPs).UnsortedList() {
195-
eg := eip.egressIPs[ip]
196-
if eg == nil {
197-
continue
198-
}
199-
eg.deleteNode(node)
200-
eip.syncEgressIP(eg)
217+
eip.deleteNode(ip, node)
201218
}
219+
220+
eip.syncEgressIPs()
202221
}
203222

204223
func (eip *egressIPWatcher) watchNetNamespaces() {
@@ -214,10 +233,8 @@ func (eip *egressIPWatcher) handleAddOrUpdateNetNamespace(obj, _ interface{}, ev
214233
if len(netns.EgressIPs) > 1 {
215234
glog.Warningf("Ignoring extra EgressIPs (%v) in NetNamespace %q", netns.EgressIPs[1:], netns.Name)
216235
}
217-
eip.updateNamespaceEgress(netns.NetID, netns.EgressIPs[0])
218-
} else {
219-
eip.deleteNamespaceEgress(netns.NetID)
220236
}
237+
eip.updateNamespaceEgress(netns.NetID, netns.EgressIPs)
221238
}
222239

223240
func (eip *egressIPWatcher) handleDeleteNetNamespace(obj interface{}) {
@@ -227,126 +244,128 @@ func (eip *egressIPWatcher) handleDeleteNetNamespace(obj interface{}) {
227244
eip.deleteNamespaceEgress(netns.NetID)
228245
}
229246

230-
func (eip *egressIPWatcher) updateNamespaceEgress(vnid uint32, egressIP string) {
247+
func (eip *egressIPWatcher) updateNamespaceEgress(vnid uint32, egressIPs []string) {
231248
eip.Lock()
232249
defer eip.Unlock()
233250

234251
ns := eip.namespacesByVNID[vnid]
235252
if ns == nil {
236-
if egressIP == "" {
253+
if len(egressIPs) == 0 {
237254
return
238255
}
239256
ns = &namespaceEgress{vnid: vnid}
240257
eip.namespacesByVNID[vnid] = ns
241-
} else if egressIP == "" {
258+
} else if len(egressIPs) == 0 {
242259
delete(eip.namespacesByVNID, vnid)
243260
}
244261

245-
if ns.requestedIP == egressIP {
246-
return
247-
}
262+
oldRequestedIPs := sets.NewString(ns.requestedIPs...)
263+
newRequestedIPs := sets.NewString(egressIPs...)
264+
ns.requestedIPs = egressIPs
248265

249-
if ns.requestedIP != "" {
250-
eg := eip.egressIPs[ns.requestedIP]
251-
if eg != nil {
252-
eg.deleteNamespace(ns)
253-
eip.syncEgressIP(eg)
254-
}
266+
// Process new and removed EgressIPs
267+
for _, ip := range newRequestedIPs.Difference(oldRequestedIPs).UnsortedList() {
268+
eip.addNamespace(ip, ns)
255269
}
256-
257-
ns.requestedIP = egressIP
258-
if egressIP == "" {
259-
return
270+
for _, ip := range oldRequestedIPs.Difference(newRequestedIPs).UnsortedList() {
271+
eip.deleteNamespace(ip, ns)
260272
}
261273

262-
eg := eip.ensureEgressIPInfo(egressIP)
263-
eg.addNamespace(ns)
264-
eip.syncEgressIP(eg)
274+
// Make sure we update OVS even if nothing was added or removed; the order might
275+
// have changed
276+
eip.changedNamespaces = append(eip.changedNamespaces, ns)
277+
278+
eip.syncEgressIPs()
265279
}
266280

267281
func (eip *egressIPWatcher) deleteNamespaceEgress(vnid uint32) {
268-
eip.updateNamespaceEgress(vnid, "")
282+
eip.updateNamespaceEgress(vnid, nil)
269283
}
270284

271-
func (eip *egressIPWatcher) syncEgressIP(eg *egressIPInfo) {
272-
assignedNodeIPChanged := eip.syncEgressIPTablesState(eg)
273-
eip.syncEgressOVSState(eg, assignedNodeIPChanged)
285+
func (eip *egressIPWatcher) syncEgressIPs() {
286+
changedEgressIPs := make(map[*egressIPInfo]bool)
287+
for _, eg := range eip.changedEgressIPs {
288+
changedEgressIPs[eg] = true
289+
}
290+
eip.changedEgressIPs = eip.changedEgressIPs[:0]
291+
292+
changedNamespaces := make(map[*namespaceEgress]bool)
293+
for _, ns := range eip.changedNamespaces {
294+
changedNamespaces[ns] = true
295+
}
296+
eip.changedNamespaces = eip.changedNamespaces[:0]
297+
298+
for eg := range changedEgressIPs {
299+
eip.syncEgressNodeState(eg)
300+
}
301+
302+
for ns := range changedNamespaces {
303+
err := eip.syncEgressNamespaceState(ns)
304+
if err != nil {
305+
utilruntime.HandleError(fmt.Errorf("Error updating Namespace egress rules for VNID %d: %v", ns.vnid, err))
306+
}
307+
}
274308
}
275309

276-
func (eip *egressIPWatcher) syncEgressIPTablesState(eg *egressIPInfo) bool {
310+
func (eip *egressIPWatcher) syncEgressNodeState(eg *egressIPInfo) {
277311
// The egressIPInfo should have an assigned node IP if and only if the
278312
// egress IP is active (ie, it is assigned to exactly 1 node and exactly
279313
// 1 namespace).
280314
egressIPActive := (len(eg.nodes) == 1 && len(eg.namespaces) == 1)
281-
assignedNodeIPChanged := false
282315
if egressIPActive && eg.assignedNodeIP != eg.nodes[0].nodeIP {
316+
glog.V(4).Infof("Assigning egress IP %s to node %s", eg.ip, eg.nodes[0].nodeIP)
283317
eg.assignedNodeIP = eg.nodes[0].nodeIP
284318
eg.assignedIPTablesMark = getMarkForVNID(eg.namespaces[0].vnid, eip.masqueradeBit)
285-
assignedNodeIPChanged = true
286319
if eg.assignedNodeIP == eip.localIP {
287320
if err := eip.assignEgressIP(eg.ip, eg.assignedIPTablesMark); err != nil {
288321
utilruntime.HandleError(fmt.Errorf("Error assigning Egress IP %q: %v", eg.ip, err))
289322
eg.assignedNodeIP = ""
290323
}
291324
}
292325
} else if !egressIPActive && eg.assignedNodeIP != "" {
326+
glog.V(4).Infof("Removing egress IP %s from node %s", eg.ip, eg.assignedNodeIP)
293327
if eg.assignedNodeIP == eip.localIP {
294328
if err := eip.releaseEgressIP(eg.ip, eg.assignedIPTablesMark); err != nil {
295329
utilruntime.HandleError(fmt.Errorf("Error releasing Egress IP %q: %v", eg.ip, err))
296330
}
297331
}
298332
eg.assignedNodeIP = ""
299333
eg.assignedIPTablesMark = ""
300-
assignedNodeIPChanged = true
334+
} else if !egressIPActive {
335+
glog.V(4).Infof("Egress IP %s is not assignable (%d namespaces, %d nodes)", eg.ip, len(eg.namespaces), len(eg.nodes))
301336
}
302-
return assignedNodeIPChanged
303337
}
304338

305-
func (eip *egressIPWatcher) syncEgressOVSState(eg *egressIPInfo, assignedNodeIPChanged bool) {
306-
var blockedVNIDs map[uint32]bool
307-
308-
// If multiple namespaces are assigned to the same EgressIP, we need to block
309-
// outgoing traffic from all of them.
310-
if len(eg.namespaces) > 1 {
311-
eg.assignedVNID = 0
312-
blockedVNIDs = make(map[uint32]bool)
313-
for _, ns := range eg.namespaces {
314-
blockedVNIDs[ns.vnid] = true
315-
if !eg.blockedVNIDs[ns.vnid] {
316-
err := eip.oc.SetNamespaceEgressDropped(ns.vnid)
317-
if err != nil {
318-
utilruntime.HandleError(fmt.Errorf("Error updating Namespace egress rules: %v", err))
319-
}
320-
}
321-
}
339+
func (eip *egressIPWatcher) syncEgressNamespaceState(ns *namespaceEgress) error {
340+
if len(ns.requestedIPs) == 0 {
341+
return eip.oc.SetNamespaceEgressNormal(ns.vnid)
322342
}
323343

324-
// If we have, or had, a single egress namespace, then update the OVS flows if
325-
// something has changed
326-
var err error
327-
if len(eg.namespaces) == 1 && (eg.assignedVNID != eg.namespaces[0].vnid || assignedNodeIPChanged) {
328-
eg.assignedVNID = eg.namespaces[0].vnid
329-
delete(eg.blockedVNIDs, eg.assignedVNID)
330-
err = eip.oc.SetNamespaceEgressViaEgressIP(eg.assignedVNID, eg.assignedNodeIP, getMarkForVNID(eg.assignedVNID, eip.masqueradeBit))
331-
} else if len(eg.namespaces) == 0 && eg.assignedVNID != 0 {
332-
err = eip.oc.SetNamespaceEgressNormal(eg.assignedVNID)
333-
eg.assignedVNID = 0
334-
}
335-
if err != nil {
336-
utilruntime.HandleError(fmt.Errorf("Error updating Namespace egress rules: %v", err))
337-
}
338-
339-
// If we previously had blocked VNIDs, we need to unblock any that have been removed
340-
// from the duplicates list
341-
for vnid := range eg.blockedVNIDs {
342-
if !blockedVNIDs[vnid] {
343-
err := eip.oc.SetNamespaceEgressNormal(vnid)
344-
if err != nil {
345-
utilruntime.HandleError(fmt.Errorf("Error updating Namespace egress rules: %v", err))
344+
var active *egressIPInfo
345+
for i, ip := range ns.requestedIPs {
346+
eg := eip.egressIPs[ip]
347+
if eg == nil {
348+
continue
349+
}
350+
if len(eg.namespaces) > 1 {
351+
active = nil
352+
glog.V(4).Infof("VNID %d gets no egress due to multiply-assigned egress IP %s", ns.vnid, eg.ip)
353+
break
354+
}
355+
if active == nil && i == 0 {
356+
if eg.assignedNodeIP == "" {
357+
glog.V(4).Infof("VNID %d cannot use unassigned egress IP %s", ns.vnid, eg.ip)
358+
} else {
359+
active = eg
346360
}
347361
}
348362
}
349-
eg.blockedVNIDs = blockedVNIDs
363+
364+
if active != nil {
365+
return eip.oc.SetNamespaceEgressViaEgressIP(ns.vnid, active.assignedNodeIP, active.assignedIPTablesMark)
366+
} else {
367+
return eip.oc.SetNamespaceEgressDropped(ns.vnid)
368+
}
350369
}
351370

352371
func (eip *egressIPWatcher) assignEgressIP(egressIP, mark string) error {

0 commit comments

Comments
 (0)