@@ -20,6 +20,9 @@ limitations under the License.
20
20
package conntrack
21
21
22
22
import (
23
+ "github.com/vishvananda/netlink"
24
+ "golang.org/x/sys/unix"
25
+
23
26
v1 "k8s.io/api/core/v1"
24
27
"k8s.io/apimachinery/pkg/util/sets"
25
28
"k8s.io/klog/v2"
@@ -40,6 +43,7 @@ func CleanStaleEntries(ct Interface, svcPortMap proxy.ServicePortMap,
40
43
// may create "black hole" entries for that IP+port. When the service gets endpoints we
41
44
// need to delete those entries so further traffic doesn't get dropped.
42
45
func deleteStaleServiceConntrackEntries (ct Interface , svcPortMap proxy.ServicePortMap , serviceUpdateResult proxy.UpdateServiceMapResult , endpointsUpdateResult proxy.UpdateEndpointsMapResult ) {
46
+ var filters []netlink.CustomConntrackFilter
43
47
conntrackCleanupServiceIPs := serviceUpdateResult .DeletedUDPClusterIPs
44
48
conntrackCleanupServiceNodePorts := sets .New [int ]()
45
49
isIPv6 := false
@@ -48,6 +52,7 @@ func deleteStaleServiceConntrackEntries(ct Interface, svcPortMap proxy.ServicePo
48
52
// a UDP service that changes from 0 to non-0 endpoints is newly active.
49
53
for _ , svcPortName := range endpointsUpdateResult .NewlyActiveUDPServices {
50
54
if svcInfo , ok := svcPortMap [svcPortName ]; ok {
55
+ isIPv6 = netutils .IsIPv6 (svcInfo .ClusterIP ())
51
56
klog .V (4 ).InfoS ("Newly-active UDP service may have stale conntrack entries" , "servicePortName" , svcPortName )
52
57
conntrackCleanupServiceIPs .Insert (svcInfo .ClusterIP ().String ())
53
58
for _ , extIP := range svcInfo .ExternalIPs () {
@@ -59,57 +64,120 @@ func deleteStaleServiceConntrackEntries(ct Interface, svcPortMap proxy.ServicePo
59
64
nodePort := svcInfo .NodePort ()
60
65
if svcInfo .Protocol () == v1 .ProtocolUDP && nodePort != 0 {
61
66
conntrackCleanupServiceNodePorts .Insert (nodePort )
62
- isIPv6 = netutils .IsIPv6 (svcInfo .ClusterIP ())
63
67
}
64
68
}
65
69
}
66
70
67
71
klog .V (4 ).InfoS ("Deleting conntrack stale entries for services" , "IPs" , conntrackCleanupServiceIPs .UnsortedList ())
68
72
for _ , svcIP := range conntrackCleanupServiceIPs .UnsortedList () {
69
- if err := ct .ClearEntriesForIP (svcIP , v1 .ProtocolUDP ); err != nil {
70
- klog .ErrorS (err , "Failed to delete stale service connections" , "IP" , svcIP )
71
- }
73
+ filters = append (filters , filterForIP (svcIP , v1 .ProtocolUDP ))
72
74
}
73
75
klog .V (4 ).InfoS ("Deleting conntrack stale entries for services" , "nodePorts" , conntrackCleanupServiceNodePorts .UnsortedList ())
74
76
for _ , nodePort := range conntrackCleanupServiceNodePorts .UnsortedList () {
75
- err := ct .ClearEntriesForPort (nodePort , isIPv6 , v1 .ProtocolUDP )
76
- if err != nil {
77
- klog .ErrorS (err , "Failed to clear udp conntrack" , "nodePort" , nodePort )
78
- }
77
+ filters = append (filters , filterForPort (nodePort , v1 .ProtocolUDP ))
78
+ }
79
+
80
+ if err := ct .ClearEntries (getUnixIPFamily (isIPv6 ), filters ... ); err != nil {
81
+ klog .ErrorS (err , "Failed to delete stale service connections" )
79
82
}
80
83
}
81
84
82
85
// deleteStaleEndpointConntrackEntries takes care of flushing stale conntrack entries related
83
86
// to UDP endpoints. After a UDP endpoint is removed we must flush any conntrack entries
84
87
// for it so that if the same client keeps sending, the packets will get routed to a new endpoint.
85
88
func deleteStaleEndpointConntrackEntries (ct Interface , svcPortMap proxy.ServicePortMap , endpointsUpdateResult proxy.UpdateEndpointsMapResult ) {
89
+ var filters []netlink.CustomConntrackFilter
90
+ isIPv6 := false
86
91
for _ , epSvcPair := range endpointsUpdateResult .DeletedUDPEndpoints {
87
92
if svcInfo , ok := svcPortMap [epSvcPair .ServicePortName ]; ok {
93
+ isIPv6 = netutils .IsIPv6 (svcInfo .ClusterIP ())
88
94
endpointIP := proxyutil .IPPart (epSvcPair .Endpoint )
89
95
nodePort := svcInfo .NodePort ()
90
- var err error
91
96
if nodePort != 0 {
92
- err = ct .ClearEntriesForPortNAT (endpointIP , nodePort , v1 .ProtocolUDP )
93
- if err != nil {
94
- klog .ErrorS (err , "Failed to delete nodeport-related endpoint connections" , "servicePortName" , epSvcPair .ServicePortName )
95
- }
96
- }
97
- err = ct .ClearEntriesForNAT (svcInfo .ClusterIP ().String (), endpointIP , v1 .ProtocolUDP )
98
- if err != nil {
99
- klog .ErrorS (err , "Failed to delete endpoint connections" , "servicePortName" , epSvcPair .ServicePortName )
97
+ filters = append (filters , filterForPortNAT (endpointIP , nodePort , v1 .ProtocolUDP ))
98
+
100
99
}
100
+ filters = append (filters , filterForNAT (svcInfo .ClusterIP ().String (), endpointIP , v1 .ProtocolUDP ))
101
101
for _ , extIP := range svcInfo .ExternalIPs () {
102
- err := ct .ClearEntriesForNAT (extIP .String (), endpointIP , v1 .ProtocolUDP )
103
- if err != nil {
104
- klog .ErrorS (err , "Failed to delete endpoint connections for externalIP" , "servicePortName" , epSvcPair .ServicePortName , "externalIP" , extIP )
105
- }
102
+ filters = append (filters , filterForNAT (extIP .String (), endpointIP , v1 .ProtocolUDP ))
106
103
}
107
104
for _ , lbIP := range svcInfo .LoadBalancerVIPs () {
108
- err := ct .ClearEntriesForNAT (lbIP .String (), endpointIP , v1 .ProtocolUDP )
109
- if err != nil {
110
- klog .ErrorS (err , "Failed to delete endpoint connections for LoadBalancerIP" , "servicePortName" , epSvcPair .ServicePortName , "loadBalancerIP" , lbIP )
111
- }
105
+ filters = append (filters , filterForNAT (lbIP .String (), endpointIP , v1 .ProtocolUDP ))
112
106
}
113
107
}
114
108
}
109
+
110
+ if err := ct .ClearEntries (getUnixIPFamily (isIPv6 ), filters ... ); err != nil {
111
+ klog .ErrorS (err , "Failed to delete stale endpoint connections" )
112
+ }
113
+ }
114
+
115
+ // getUnixIPFamily returns the unix IPFamily constant.
116
+ func getUnixIPFamily (isIPv6 bool ) uint8 {
117
+ if isIPv6 {
118
+ return unix .AF_INET6
119
+ }
120
+ return unix .AF_INET
121
+ }
122
+
123
+ // protocolMap maps v1.Protocol to the Assigned Internet Protocol Number.
124
+ // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
125
+ var protocolMap = map [v1.Protocol ]uint8 {
126
+ v1 .ProtocolTCP : unix .IPPROTO_TCP ,
127
+ v1 .ProtocolUDP : unix .IPPROTO_UDP ,
128
+ v1 .ProtocolSCTP : unix .IPPROTO_SCTP ,
129
+ }
130
+
131
+ // filterForIP returns *conntrackFilter to delete the conntrack entries for connections
132
+ // specified by the destination IP (original direction).
133
+ func filterForIP (ip string , protocol v1.Protocol ) * conntrackFilter {
134
+ klog .V (4 ).InfoS ("Adding conntrack filter for cleanup" , "org-dst" , ip , "protocol" , protocol )
135
+ return & conntrackFilter {
136
+ protocol : protocolMap [protocol ],
137
+ original : & connectionTuple {
138
+ dstIP : netutils .ParseIPSloppy (ip ),
139
+ },
140
+ }
141
+ }
142
+
143
+ // filterForPort returns *conntrackFilter to delete the conntrack entries for connections
144
+ // specified by the destination Port (original direction).
145
+ func filterForPort (port int , protocol v1.Protocol ) * conntrackFilter {
146
+ klog .V (4 ).InfoS ("Adding conntrack filter for cleanup" , "org-port-dst" , port , "protocol" , protocol )
147
+ return & conntrackFilter {
148
+ protocol : protocolMap [protocol ],
149
+ original : & connectionTuple {
150
+ dstPort : uint16 (port ),
151
+ },
152
+ }
153
+ }
154
+
155
+ // filterForNAT returns *conntrackFilter to delete the conntrack entries for connections
156
+ // specified by the destination IP (original direction) and source IP (reply direction).
157
+ func filterForNAT (origin , dest string , protocol v1.Protocol ) * conntrackFilter {
158
+ klog .V (4 ).InfoS ("Adding conntrack filter for cleanup" , "org-dst" , origin , "reply-src" , dest , "protocol" , protocol )
159
+ return & conntrackFilter {
160
+ protocol : protocolMap [protocol ],
161
+ original : & connectionTuple {
162
+ dstIP : netutils .ParseIPSloppy (origin ),
163
+ },
164
+ reply : & connectionTuple {
165
+ srcIP : netutils .ParseIPSloppy (dest ),
166
+ },
167
+ }
168
+ }
169
+
170
+ // filterForPortNAT returns *conntrackFilter to delete the conntrack entries for connections
171
+ // specified by the destination Port (original direction) and source IP (reply direction).
172
+ func filterForPortNAT (dest string , port int , protocol v1.Protocol ) * conntrackFilter {
173
+ klog .V (4 ).InfoS ("Adding conntrack filter for cleanup" , "org-port-dst" , port , "reply-src" , dest , "protocol" , protocol )
174
+ return & conntrackFilter {
175
+ protocol : protocolMap [protocol ],
176
+ original : & connectionTuple {
177
+ dstPort : uint16 (port ),
178
+ },
179
+ reply : & connectionTuple {
180
+ srcIP : netutils .ParseIPSloppy (dest ),
181
+ },
182
+ }
115
183
}
0 commit comments