5
5
"fmt"
6
6
"net"
7
7
"os"
8
- "os/exec"
9
8
"path"
10
9
"strconv"
11
10
"strings"
@@ -24,8 +23,10 @@ import (
24
23
)
25
24
26
25
const (
27
- ipv4NetMaskBits = 32
28
- ipv6NetMaskBits = 128
26
+ ipv4NetMaskBits = 32
27
+ ipv4DefaultRoute = "0.0.0.0/0"
28
+ ipv6NetMaskBits = 128
29
+ ipv6DefaultRoute = "::/0"
29
30
30
31
// TODO: it's bad to rely on eth0 here. While this is inside the container's namespace and is determined by the
31
32
// container runtime and so far we've been able to count on this being reliably set to eth0, it is possible that
@@ -67,7 +68,6 @@ type netlinkCalls interface {
67
68
68
69
func (ln * linuxNetworking ) ipAddrDel (iface netlink.Link , ip string , nodeIP string ) error {
69
70
var netMask net.IPMask
70
- var ipRouteCmdArgs []string
71
71
parsedIP := net .ParseIP (ip )
72
72
parsedNodeIP := net .ParseIP (nodeIP )
73
73
if parsedIP .To4 () != nil {
@@ -77,7 +77,6 @@ func (ln *linuxNetworking) ipAddrDel(iface netlink.Link, ip string, nodeIP strin
77
77
}
78
78
79
79
netMask = net .CIDRMask (ipv4NetMaskBits , ipv4NetMaskBits )
80
- ipRouteCmdArgs = make ([]string , 0 )
81
80
} else {
82
81
// If the IP family of the NodeIP and the VIP IP don't match, we can't proceed
83
82
if parsedNodeIP .To4 () != nil {
@@ -90,7 +89,6 @@ func (ln *linuxNetworking) ipAddrDel(iface netlink.Link, ip string, nodeIP strin
90
89
}
91
90
92
91
netMask = net .CIDRMask (ipv6NetMaskBits , ipv6NetMaskBits )
93
- ipRouteCmdArgs = []string {"-6" }
94
92
}
95
93
96
94
naddr := & netlink.Addr {IPNet : & net.IPNet {IP : parsedIP , Mask : netMask }, Scope : syscall .RT_SCOPE_LINK }
@@ -108,13 +106,20 @@ func (ln *linuxNetworking) ipAddrDel(iface netlink.Link, ip string, nodeIP strin
108
106
109
107
// Delete VIP addition to "local" rt table also, fail silently if not found (DSR special case)
110
108
// #nosec G204
111
- ipRouteCmdArgs = append (ipRouteCmdArgs , "route" , "delete" , "local" , ip , "dev" , KubeDummyIf ,
112
- "table" , "local" , "proto" , "kernel" , "scope" , "host" , "src" , nodeIP , "table" , "local" )
113
- out , err := exec .Command ("ip" , ipRouteCmdArgs ... ).CombinedOutput ()
109
+ nRoute := & netlink.Route {
110
+ Type : unix .RTN_LOCAL ,
111
+ Dst : & net.IPNet {IP : parsedIP , Mask : netMask },
112
+ LinkIndex : iface .Attrs ().Index ,
113
+ Table : syscall .RT_TABLE_LOCAL ,
114
+ Protocol : unix .RTPROT_KERNEL ,
115
+ Scope : syscall .RT_SCOPE_HOST ,
116
+ Src : parsedNodeIP ,
117
+ }
118
+ err = netlink .RouteDel (nRoute )
114
119
if err != nil {
115
- if ! strings .Contains (string ( out ), "No such process" ) {
116
- klog .Errorf ("Failed to delete route to service VIP %s configured on %s. Error: %v, Output: %s " ,
117
- ip , KubeDummyIf , err , out )
120
+ if ! strings .Contains (err . Error ( ), "no such process" ) {
121
+ klog .Errorf ("Failed to delete route to service VIP %s configured on %s. Error: %v" ,
122
+ ip , iface . Attrs (). Name , err )
118
123
} else {
119
124
klog .Warningf ("got a No such process error while trying to remove route: %v (this is not normally bad " +
120
125
"enough to stop processing)" , err )
@@ -130,7 +135,6 @@ func (ln *linuxNetworking) ipAddrDel(iface netlink.Link, ip string, nodeIP strin
130
135
// inside the container.
131
136
func (ln * linuxNetworking ) ipAddrAdd (iface netlink.Link , ip string , nodeIP string , addRoute bool ) error {
132
137
var netMask net.IPMask
133
- var ipRouteCmdArgs []string
134
138
var isIPv6 bool
135
139
parsedIP := net .ParseIP (ip )
136
140
parsedNodeIP := net .ParseIP (nodeIP )
@@ -141,7 +145,6 @@ func (ln *linuxNetworking) ipAddrAdd(iface netlink.Link, ip string, nodeIP strin
141
145
}
142
146
143
147
netMask = net .CIDRMask (ipv4NetMaskBits , ipv4NetMaskBits )
144
- ipRouteCmdArgs = make ([]string , 0 )
145
148
isIPv6 = false
146
149
} else {
147
150
// If we're supposed to add a route and the IP family of the NodeIP and the VIP IP don't match, we can't proceed
@@ -150,11 +153,11 @@ func (ln *linuxNetworking) ipAddrAdd(iface netlink.Link, ip string, nodeIP strin
150
153
}
151
154
152
155
netMask = net .CIDRMask (ipv6NetMaskBits , ipv6NetMaskBits )
153
- ipRouteCmdArgs = []string {"-6" }
154
156
isIPv6 = true
155
157
}
156
158
157
- naddr := & netlink.Addr {IPNet : & net.IPNet {IP : parsedIP , Mask : netMask }, Scope : syscall .RT_SCOPE_LINK }
159
+ ipPrefix := & net.IPNet {IP : parsedIP , Mask : netMask }
160
+ naddr := & netlink.Addr {IPNet : ipPrefix , Scope : syscall .RT_SCOPE_LINK }
158
161
err := netlink .AddrAdd (iface , naddr )
159
162
if err != nil && err .Error () != IfaceHasAddr {
160
163
klog .Errorf ("failed to assign cluster ip %s to dummy interface: %s" , naddr .IP .String (), err .Error ())
@@ -169,16 +172,24 @@ func (ln *linuxNetworking) ipAddrAdd(iface netlink.Link, ip string, nodeIP strin
169
172
return nil
170
173
}
171
174
172
- // TODO: netlink.RouteReplace which is replacement for below command is not working as expected. Call succeeds but
173
- // route is not replaced. For now do it with command.
174
- // #nosec G204
175
- ipRouteCmdArgs = append (ipRouteCmdArgs , "route" , "replace" , "local" , ip , "dev" , KubeDummyIf ,
176
- "table" , "local" , "proto" , "kernel" , "scope" , "host" , "src" , nodeIP , "table" , "local" )
177
-
178
- out , err := exec .Command ("ip" , ipRouteCmdArgs ... ).CombinedOutput ()
175
+ kubeDummyLink , err := netlink .LinkByName (KubeDummyIf )
179
176
if err != nil {
180
- klog .Errorf ("Failed to replace route to service VIP %s configured on %s. Error: %v, Output: %s" ,
181
- ip , KubeDummyIf , err , out )
177
+ klog .Errorf ("failed to get %s link due to %v" , KubeDummyIf , err )
178
+ return err
179
+ }
180
+ nRoute := & netlink.Route {
181
+ Type : unix .RTN_LOCAL ,
182
+ Dst : ipPrefix ,
183
+ LinkIndex : kubeDummyLink .Attrs ().Index ,
184
+ Table : syscall .RT_TABLE_LOCAL ,
185
+ Protocol : unix .RTPROT_KERNEL ,
186
+ Scope : syscall .RT_SCOPE_HOST ,
187
+ Src : parsedNodeIP ,
188
+ }
189
+ err = netlink .RouteReplace (nRoute )
190
+ if err != nil {
191
+ klog .Errorf ("Failed to replace route to service VIP %s configured on %s. Error: %v" ,
192
+ ip , KubeDummyIf , err )
182
193
return err
183
194
}
184
195
@@ -482,60 +493,108 @@ func (ln *linuxNetworking) setupPolicyRoutingForDSR(setupIPv4, setupIPv6 bool) e
482
493
return fmt .Errorf ("failed to setup policy routing required for DSR due to %v" , err )
483
494
}
484
495
496
+ loNetLink , err := netlink .LinkByName ("lo" )
497
+ if err != nil {
498
+ return fmt .Errorf ("failed to get loopback interface due to %v" , err )
499
+ }
500
+
485
501
if setupIPv4 {
486
- out , err := exec .Command ("ip" , "route" , "list" , "table" , customDSRRouteTableID ).Output ()
487
- if err != nil || ! strings .Contains (string (out ), " lo " ) {
488
- if err = exec .Command ("ip" , "route" , "add" , "local" , "default" , "dev" , "lo" , "table" ,
489
- customDSRRouteTableID ).Run (); err != nil {
490
- return fmt .Errorf ("failed to add route in custom route table due to: %v" , err )
502
+ nFamily := netlink .FAMILY_V4
503
+ _ , defaultRouteCIDR , err := net .ParseCIDR (ipv4DefaultRoute )
504
+ if err != nil {
505
+ return fmt .Errorf ("failed to parse default (%s) route (this is statically defined, so if you see this " +
506
+ "error please report because something has gone very wrong) due to: %v" , ipv4DefaultRoute , err )
507
+ }
508
+ nRoute := & netlink.Route {
509
+ Type : unix .RTN_LOCAL ,
510
+ Dst : defaultRouteCIDR ,
511
+ LinkIndex : loNetLink .Attrs ().Index ,
512
+ Table : customDSRRouteTableID ,
513
+ }
514
+ routes , err := netlink .RouteListFiltered (nFamily , nRoute , netlink .RT_FILTER_TABLE | netlink .RT_FILTER_OIF )
515
+ if err != nil || len (routes ) < 1 {
516
+ err = netlink .RouteAdd (nRoute )
517
+ if err != nil {
518
+ return fmt .Errorf ("failed to add route to custom route table for DSR due to: %v" , err )
491
519
}
492
520
}
493
521
}
522
+
494
523
if setupIPv6 {
495
- out , err := exec .Command ("ip" , "-6" , "route" , "list" , "table" , customDSRRouteTableID ).Output ()
496
- if err != nil || ! strings .Contains (string (out ), " lo " ) {
497
- if err = exec .Command ("ip" , "-6" , "route" , "add" , "local" , "default" , "dev" , "lo" , "table" ,
498
- customDSRRouteTableID ).Run (); err != nil {
499
- return fmt .Errorf ("failed to add route in custom route table due to: %v" , err )
524
+ nFamily := netlink .FAMILY_V6
525
+ _ , defaultRouteCIDR , err := net .ParseCIDR (ipv6DefaultRoute )
526
+ if err != nil {
527
+ return fmt .Errorf ("failed to parse default (%s) route (this is statically defined, so if you see this " +
528
+ "error please report because something has gone very wrong) due to: %v" , ipv6DefaultRoute , err )
529
+ }
530
+ nRoute := & netlink.Route {
531
+ Type : unix .RTN_LOCAL ,
532
+ Dst : defaultRouteCIDR ,
533
+ LinkIndex : loNetLink .Attrs ().Index ,
534
+ Table : customDSRRouteTableID ,
535
+ }
536
+ routes , err := netlink .RouteListFiltered (nFamily , nRoute , netlink .RT_FILTER_TABLE | netlink .RT_FILTER_OIF )
537
+ if err != nil || len (routes ) < 1 {
538
+ err = netlink .RouteAdd (nRoute )
539
+ if err != nil {
540
+ return fmt .Errorf ("failed to add route to custom route table for DSR due to: %v" , err )
500
541
}
501
542
}
502
543
}
544
+
503
545
return nil
504
546
}
505
547
506
548
// For DSR it is required that node needs to know how to route external IP. Otherwise when endpoint
507
549
// directly responds back with source IP as external IP kernel will treat as martian packet.
508
550
// To prevent martian packets add route to external IP through the `kube-bridge` interface
509
551
// setupRoutesForExternalIPForDSR: setups routing so that kernel does not think return packets as martians
510
-
511
552
func (ln * linuxNetworking ) setupRoutesForExternalIPForDSR (serviceInfoMap serviceInfoMap ,
512
553
setupIPv4 , setupIPv6 bool ) error {
513
554
err := utils .RouteTableAdd (externalIPRouteTableID , externalIPRouteTableName )
514
555
if err != nil {
515
556
return fmt .Errorf ("failed to setup policy routing required for DSR due to %v" , err )
516
557
}
517
558
518
- setupIPRulesAndRoutes := func (ipArgs []string ) error {
519
- out , err := runIPCommandsWithArgs (ipArgs , "rule" , "list" ).Output ()
559
+ setupIPRulesAndRoutes := func (isIPv6 bool ) error {
560
+ nFamily := netlink .FAMILY_V4
561
+ _ , defaultPrefixCIDR , err := net .ParseCIDR (ipv4DefaultRoute )
562
+ if isIPv6 {
563
+ nFamily = netlink .FAMILY_V6
564
+ _ , defaultPrefixCIDR , err = net .ParseCIDR (ipv6DefaultRoute )
565
+ }
566
+ if err != nil {
567
+ return fmt .Errorf ("failed to parse default route (this is statically defined, so if you see this " +
568
+ "error please report because something has gone very wrong) due to: %v" , err )
569
+ }
570
+
571
+ nRule := & netlink.Rule {
572
+ Priority : defaultDSRPolicyRulePriority ,
573
+ Src : defaultPrefixCIDR ,
574
+ Table : externalIPRouteTableID ,
575
+ }
576
+ rules , err := netlink .RuleListFiltered (nFamily , nRule ,
577
+ netlink .RT_FILTER_TABLE | netlink .RT_FILTER_SRC | netlink .RT_FILTER_PRIORITY )
520
578
if err != nil {
521
- return fmt .Errorf ("failed to verify if `ip rule add prio 32765 from all lookup external_ip` exists due to: %v" ,
522
- err )
579
+ return fmt .Errorf ("failed to list rule for external IP's and verify if `ip rule add prio 32765 from all " +
580
+ "lookup external_ip` exists due to: %v" , err )
523
581
}
524
582
525
- if ! strings .Contains (string (out ), externalIPRouteTableName ) &&
526
- ! strings .Contains (string (out ), externalIPRouteTableID ) {
527
- err = runIPCommandsWithArgs (ipArgs , "rule" , "add" , "prio" , "32765" , "from" , "all" , "lookup" ,
528
- externalIPRouteTableID ).Run ()
583
+ if len (rules ) < 1 {
584
+ err = netlink .RuleAdd (nRule )
529
585
if err != nil {
530
586
klog .Infof ("Failed to add policy rule `ip rule add prio 32765 from all lookup external_ip` due to %v" ,
531
- err . Error () )
587
+ err )
532
588
return fmt .Errorf ("failed to add policy rule `ip rule add prio 32765 from all lookup external_ip` " +
533
589
"due to %v" , err )
534
590
}
535
591
}
536
592
537
- out , _ = runIPCommandsWithArgs (ipArgs , "route" , "list" , "table" , externalIPRouteTableID ).Output ()
538
- outStr := string (out )
593
+ kubeBridgeLink , err := netlink .LinkByName (KubeBridgeIf )
594
+ if err != nil {
595
+ return fmt .Errorf ("failed to get kube-bridge interface due to %v" , err )
596
+ }
597
+
539
598
activeExternalIPs := make (map [string ]bool )
540
599
for _ , svc := range serviceInfoMap {
541
600
for _ , externalIP := range svc .externalIPs {
@@ -548,9 +607,21 @@ func (ln *linuxNetworking) setupRoutesForExternalIPForDSR(serviceInfoMap service
548
607
549
608
activeExternalIPs [externalIP ] = true
550
609
551
- if ! strings .Contains (outStr , externalIP ) {
552
- if err = runIPCommandsWithArgs (ipArgs , "route" , "add" , externalIP , "dev" , "kube-bridge" , "table" ,
553
- externalIPRouteTableID ).Run (); err != nil {
610
+ nSrcIP := net .ParseIP (externalIP )
611
+ nRoute := & netlink.Route {
612
+ Src : nSrcIP ,
613
+ LinkIndex : kubeBridgeLink .Attrs ().Index ,
614
+ Table : externalIPRouteTableID ,
615
+ }
616
+
617
+ routes , err := netlink .RouteListFiltered (nFamily , nRoute ,
618
+ netlink .RT_FILTER_SRC | netlink .RT_FILTER_TABLE | netlink .RT_FILTER_OIF )
619
+ if err != nil {
620
+ return fmt .Errorf ("failed to list route for external IP's due to: %s" , err )
621
+ }
622
+ if len (routes ) < 1 {
623
+ err = netlink .RouteAdd (nRoute )
624
+ if err != nil {
554
625
klog .Errorf ("Failed to add route for %s in custom route table for external IP's due to: %v" ,
555
626
externalIP , err )
556
627
continue
@@ -560,19 +631,18 @@ func (ln *linuxNetworking) setupRoutesForExternalIPForDSR(serviceInfoMap service
560
631
}
561
632
562
633
// check if there are any pbr in externalIPRouteTableID for external IP's
563
- if len (outStr ) > 0 {
564
- // clean up stale external IPs
565
- for _ , line := range strings .Split (strings .Trim (outStr , "\n " ), "\n " ) {
566
- route := strings .Split (strings .Trim (line , " " ), " " )
567
- ip := route [0 ]
568
- if ! activeExternalIPs [ip ] {
569
- args := []string {"route" , "del" , "table" , externalIPRouteTableID }
570
- args = append (args , route ... )
571
- if err = runIPCommandsWithArgs (ipArgs , args ... ).Run (); err != nil {
572
- klog .Errorf ("Failed to del route for %v in custom route table for external IP's due to: %s" ,
573
- ip , err )
574
- continue
575
- }
634
+ routes , err := netlink .RouteList (nil , nFamily )
635
+ if err != nil {
636
+ return fmt .Errorf ("failed to list route for external IP's due to: %s" , err )
637
+ }
638
+ for idx , route := range routes {
639
+ ip := route .Src .String ()
640
+ if ! activeExternalIPs [ip ] {
641
+ err = netlink .RouteDel (& routes [idx ])
642
+ if err != nil {
643
+ klog .Errorf ("Failed to del route for %v in custom route table for external IP's due to: %s" ,
644
+ ip , err )
645
+ continue
576
646
}
577
647
}
578
648
}
@@ -581,13 +651,13 @@ func (ln *linuxNetworking) setupRoutesForExternalIPForDSR(serviceInfoMap service
581
651
}
582
652
583
653
if setupIPv4 {
584
- err = setupIPRulesAndRoutes ([] string {} )
654
+ err = setupIPRulesAndRoutes (false )
585
655
if err != nil {
586
656
return err
587
657
}
588
658
}
589
659
if setupIPv6 {
590
- err = setupIPRulesAndRoutes ([] string { "-6" } )
660
+ err = setupIPRulesAndRoutes (true )
591
661
if err != nil {
592
662
return err
593
663
}
0 commit comments