blob: 3efe41b3edc83db921afd1f602bf8aaae0c8908d [file] [log] [blame]
Jian Li7d111d72019-04-12 13:58:44 +09001/*
2 * Copyright 2019-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.k8snetworking.impl;
17
Jian Lic9d0d702020-10-07 11:50:58 +090018import org.apache.commons.lang.StringUtils;
Jian Li1a2eb5d2019-08-27 02:07:05 +090019import org.onlab.packet.ARP;
Jian Li7d111d72019-04-12 13:58:44 +090020import org.onlab.packet.Ethernet;
Jian Li1a2eb5d2019-08-27 02:07:05 +090021import org.onlab.packet.Ip4Address;
Jian Li7d111d72019-04-12 13:58:44 +090022import org.onlab.packet.IpPrefix;
23import org.onosproject.cluster.ClusterService;
24import org.onosproject.cluster.LeadershipService;
25import org.onosproject.cluster.NodeId;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.k8snetworking.api.K8sFlowRuleService;
29import org.onosproject.k8snetworking.api.K8sNetwork;
30import org.onosproject.k8snetworking.api.K8sNetworkEvent;
31import org.onosproject.k8snetworking.api.K8sNetworkListener;
32import org.onosproject.k8snetworking.api.K8sNetworkService;
Jian Lic9d0d702020-10-07 11:50:58 +090033import org.onosproject.k8snode.api.K8sHost;
34import org.onosproject.k8snode.api.K8sHostService;
Jian Li7d111d72019-04-12 13:58:44 +090035import org.onosproject.k8snode.api.K8sNode;
36import org.onosproject.k8snode.api.K8sNodeEvent;
37import org.onosproject.k8snode.api.K8sNodeListener;
38import org.onosproject.k8snode.api.K8sNodeService;
Jian Li1a2eb5d2019-08-27 02:07:05 +090039import org.onosproject.net.Device;
Jian Li7d111d72019-04-12 13:58:44 +090040import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.driver.DriverService;
43import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
Jian Li1a2eb5d2019-08-27 02:07:05 +090047import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li7d111d72019-04-12 13:58:44 +090048import org.onosproject.net.packet.PacketService;
49import org.osgi.service.component.annotations.Activate;
50import org.osgi.service.component.annotations.Component;
51import org.osgi.service.component.annotations.Deactivate;
52import org.osgi.service.component.annotations.Reference;
53import org.osgi.service.component.annotations.ReferenceCardinality;
54import org.slf4j.Logger;
55
56import java.util.Objects;
Jian Lic9d0d702020-10-07 11:50:58 +090057import java.util.Set;
Jian Li7d111d72019-04-12 13:58:44 +090058import java.util.concurrent.ExecutorService;
59
60import static java.util.concurrent.Executors.newSingleThreadExecutor;
61import static org.onlab.util.Tools.groupedThreads;
Jian Li1a2eb5d2019-08-27 02:07:05 +090062import static org.onosproject.k8snetworking.api.Constants.B_CLASS;
63import static org.onosproject.k8snetworking.api.Constants.DST;
64import static org.onosproject.k8snetworking.api.Constants.HOST_PREFIX;
Jian Li7d111d72019-04-12 13:58:44 +090065import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li1a2eb5d2019-08-27 02:07:05 +090066import static org.onosproject.k8snetworking.api.Constants.LOCAL_ENTRY_TABLE;
67import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090068import static org.onosproject.k8snetworking.api.Constants.PRIORITY_GATEWAY_RULE;
Jian Lic9d0d702020-10-07 11:50:58 +090069import static org.onosproject.k8snetworking.api.Constants.PRIORITY_INTER_NODE_RULE;
Jian Li1a2eb5d2019-08-27 02:07:05 +090070import static org.onosproject.k8snetworking.api.Constants.PRIORITY_LOCAL_BRIDGE_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090071import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
Jian Li1a2eb5d2019-08-27 02:07:05 +090072import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
73import static org.onosproject.k8snetworking.api.Constants.SHIFTED_LOCAL_IP_PREFIX;
74import static org.onosproject.k8snetworking.api.Constants.SRC;
Jian Li619fa282020-09-02 14:45:35 +090075import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
Jian Li1a2eb5d2019-08-27 02:07:05 +090076import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.shiftIpDomain;
Jian Li7d111d72019-04-12 13:58:44 +090077import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
78import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
Jian Li1a2eb5d2019-08-27 02:07:05 +090079import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
80import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
81import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
82import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
Jian Li7d111d72019-04-12 13:58:44 +090083import static org.slf4j.LoggerFactory.getLogger;
84
85/**
86 * Populates switching flow rules on OVS for providing the connectivity between
87 * container and network gateway.
88 */
89@Component(immediate = true)
90public class K8sSwitchingGatewayHandler {
91
92 private final Logger log = getLogger(getClass());
93
Jian Li1a2eb5d2019-08-27 02:07:05 +090094 private static final String REQUEST = "req";
95 private static final String REPLY = "rep";
Jian Li7d111d72019-04-12 13:58:44 +090096
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected CoreService coreService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected ClusterService clusterService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected LeadershipService leadershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected DeviceService deviceService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected DriverService driverService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected PacketService packetService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected K8sFlowRuleService k8sFlowRuleService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
119 protected K8sNetworkService k8sNetworkService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
122 protected K8sNodeService k8sNodeService;
123
Jian Lic9d0d702020-10-07 11:50:58 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
125 protected K8sHostService k8sHostService;
126
Jian Li7d111d72019-04-12 13:58:44 +0900127 private final ExecutorService eventExecutor = newSingleThreadExecutor(
128 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
129 private final InternalK8sNetworkListener k8sNetworkListener =
130 new InternalK8sNetworkListener();
131 private final InternalK8sNodeListener k8sNodeListener =
132 new InternalK8sNodeListener();
133
134 private ApplicationId appId;
135 private NodeId localNodeId;
136
137 @Activate
138 protected void activate() {
139 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
140 k8sNetworkService.addListener(k8sNetworkListener);
141 k8sNodeService.addListener(k8sNodeListener);
142 localNodeId = clusterService.getLocalNode().id();
143 leadershipService.runForLeadership(appId.name());
144
145 log.info("Started");
146 }
147
148 @Deactivate
149 protected void deactivate() {
150 k8sNodeService.removeListener(k8sNodeListener);
151 k8sNetworkService.removeListener(k8sNetworkListener);
152 leadershipService.withdraw(appId.name());
153 eventExecutor.shutdown();
154
155 log.info("Stopped");
156 }
157
158 private void setGatewayRule(K8sNetwork k8sNetwork, boolean install) {
Jian Li7d111d72019-04-12 13:58:44 +0900159 for (K8sNode node : k8sNodeService.completeNodes()) {
Jian Li1a2eb5d2019-08-27 02:07:05 +0900160 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
161 .matchEthType(Ethernet.TYPE_IPV4)
162 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
163 HOST_PREFIX));
164
Jian Li7d111d72019-04-12 13:58:44 +0900165 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
166
167 if (node.hostname().equals(k8sNetwork.name())) {
Jian Lieb488ea2019-04-16 01:50:02 +0900168 tBuilder.setEthDst(node.intgBridgeMac())
Jian Li019ce6a2020-09-09 10:23:21 +0900169 .setOutput(node.intgEntryPortNum());
Jian Li7d111d72019-04-12 13:58:44 +0900170 } else {
Jian Li7d111d72019-04-12 13:58:44 +0900171 K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
172
Jian Li619fa282020-09-02 14:45:35 +0900173 tBuilder.setOutput(node.intgToTunPortNum());
174
175 // install flows into tunnel bridge
176 PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
177 k8sNetworkService, node);
178 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
179 .extension(buildExtension(
180 deviceService,
181 node.tunBridge(),
182 localNode.dataIp().getIp4Address()),
183 node.tunBridge())
184 .setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
185 .setOutput(portNum)
186 .build();
187
188 k8sFlowRuleService.setRule(
189 appId,
190 node.tunBridge(),
191 sBuilder.build(),
192 treatmentToRemote,
193 PRIORITY_GATEWAY_RULE,
194 TUN_ENTRY_TABLE,
195 install);
Jian Li7d111d72019-04-12 13:58:44 +0900196 }
197
198 k8sFlowRuleService.setRule(
199 appId,
200 node.intgBridge(),
201 sBuilder.build(),
202 tBuilder.build(),
203 PRIORITY_GATEWAY_RULE,
204 ROUTING_TABLE,
205 install);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900206
207 if (node.hostname().equals(k8sNetwork.name())) {
208 sBuilder = DefaultTrafficSelector.builder()
Jian Li019ce6a2020-09-09 10:23:21 +0900209 .matchInPort(node.intgEntryPortNum())
Jian Li1a2eb5d2019-08-27 02:07:05 +0900210 .matchEthType(Ethernet.TYPE_IPV4)
211 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
212 HOST_PREFIX));
213
214 tBuilder = DefaultTrafficTreatment.builder()
215 .setOutput(node.intgToLocalPatchPortNum());
216
217 k8sFlowRuleService.setRule(
218 appId,
219 node.intgBridge(),
220 sBuilder.build(),
221 tBuilder.build(),
222 PRIORITY_LOCAL_BRIDGE_RULE,
223 ROUTING_TABLE,
224 install);
225 }
Jian Li7d111d72019-04-12 13:58:44 +0900226 }
227 }
228
Jian Lic9d0d702020-10-07 11:50:58 +0900229 private void setInterNodeRoutingRules(K8sNetwork k8sNetwork, boolean install) {
230 K8sNode srcNode = k8sNodeService.node(k8sNetwork.name());
231
232 if (srcNode == null) {
233 return;
234 }
235
236 for (K8sNode dstNode : k8sNodeService.completeNodes()) {
237 if (StringUtils.equals(srcNode.hostname(), dstNode.hostname())) {
238 continue;
239 }
240
241 boolean sameHost = false;
242 for (K8sHost host : k8sHostService.completeHosts()) {
243 Set<String> nodeNames = host.nodeNames();
244 // if the src and dst nodes located in the same hosts,
245 // we simply do not tunnel the traffic, instead we route the traffic
246 if (nodeNames.contains(srcNode.hostname()) &&
247 nodeNames.contains(dstNode.hostname())) {
248 sameHost = true;
249 }
250 }
251
252 if (sameHost) {
253 TrafficSelector selector = DefaultTrafficSelector.builder()
254 .matchEthType(Ethernet.TYPE_IPV4)
255 .matchIPSrc(IpPrefix.valueOf(srcNode.podCidr()))
256 .matchIPDst(IpPrefix.valueOf(dstNode.podCidr()))
257 .build();
258
259 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
260 .setOutput(dstNode.tunToIntgPortNum())
261 .build();
262
263 k8sFlowRuleService.setRule(
264 appId,
265 dstNode.tunBridge(),
266 selector,
267 treatment,
268 PRIORITY_INTER_NODE_RULE,
269 TUN_ENTRY_TABLE,
270 install);
271 }
272 }
273 }
274
Jian Li1a2eb5d2019-08-27 02:07:05 +0900275 private void setLocalBridgeRules(K8sNetwork k8sNetwork, boolean install) {
276 for (K8sNode node : k8sNodeService.completeNodes()) {
277 if (node.hostname().equals(k8sNetwork.name())) {
278 setLocalBridgeRule(k8sNetwork, node, REQUEST, install);
279 setLocalBridgeRule(k8sNetwork, node, REPLY, install);
280 }
281 }
282 }
283
284 private void setLocalBridgeRule(K8sNetwork k8sNetwork, K8sNode k8sNode,
285 String type, boolean install) {
286 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
287 .matchEthType(Ethernet.TYPE_IPV4);
288 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
289
290 ExtensionTreatment loadTreatment = null;
291
292 if (REQUEST.equals(type)) {
293 loadTreatment = buildLoadExtension(deviceService.getDevice(
294 k8sNode.localBridge()), B_CLASS, SRC, SHIFTED_LOCAL_IP_PREFIX);
295 }
296
297 if (REPLY.equals(type)) {
298 loadTreatment = buildLoadExtension(deviceService.getDevice(
299 k8sNode.localBridge()), B_CLASS, DST, SHIFTED_IP_PREFIX);
300 }
301
302 tBuilder.extension(loadTreatment, k8sNode.localBridge());
303
304 if (REQUEST.equals(type)) {
305 sBuilder.matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
306 HOST_PREFIX));
307 tBuilder.setOutput(PortNumber.LOCAL);
308 }
309
310 if (REPLY.equals(type)) {
311 sBuilder.matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
312 HOST_PREFIX));
Jian Lie2a04ce2020-07-01 19:07:02 +0900313 tBuilder.setOutput(k8sNode.localToIntgPatchPortNum());
Jian Li1a2eb5d2019-08-27 02:07:05 +0900314 }
315
316 k8sFlowRuleService.setRule(
317 appId,
318 k8sNode.localBridge(),
319 sBuilder.build(),
320 tBuilder.build(),
321 PRIORITY_LOCAL_BRIDGE_RULE,
322 LOCAL_ENTRY_TABLE,
323 install);
324 }
325
326 private void setLocalBridgeArpRules(K8sNetwork k8sNetwork, boolean install) {
327 for (K8sNode node : k8sNodeService.completeNodes()) {
328 if (node.hostname().equals(k8sNetwork.name())) {
329 setLocalBridgeArpRule(k8sNetwork, node, install);
330 }
331 }
332 }
333
334 private void setLocalBridgeArpRule(K8sNetwork k8sNetwork, K8sNode k8sNode, boolean install) {
335 Device device = deviceService.getDevice(k8sNode.localBridge());
336
337 String shiftedLocalIp = shiftIpDomain(
338 k8sNetwork.gatewayIp().toString(), SHIFTED_LOCAL_IP_PREFIX);
339
340 TrafficSelector selector = DefaultTrafficSelector.builder()
341 .matchEthType(Ethernet.TYPE_ARP)
342 .matchArpOp(ARP.OP_REQUEST)
343 .matchArpTpa(Ip4Address.valueOf(shiftedLocalIp))
344 .build();
345
346 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
347 .setArpOp(ARP.OP_REPLY)
348 .extension(buildMoveEthSrcToDstExtension(device), device.id())
349 .extension(buildMoveArpShaToThaExtension(device), device.id())
350 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
351 .setArpSpa(Ip4Address.valueOf(shiftedLocalIp))
352 .setArpSha(k8sNode.intgBridgeMac())
353 .setOutput(PortNumber.IN_PORT)
354 .build();
355
356 k8sFlowRuleService.setRule(
357 appId,
358 device.id(),
359 selector,
360 treatment,
361 PRIORITY_ARP_REPLY_RULE,
362 LOCAL_ENTRY_TABLE,
363 install);
364 }
365
Jian Li7d111d72019-04-12 13:58:44 +0900366 private class InternalK8sNetworkListener implements K8sNetworkListener {
367
368 private boolean isRelevantHelper() {
369 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
370 }
371
372 @Override
373 public void event(K8sNetworkEvent event) {
374 switch (event.type()) {
375 case K8S_NETWORK_CREATED:
376 case K8S_NETWORK_UPDATED:
377 eventExecutor.execute(() -> processNetworkCreation(event));
378 break;
379 case K8S_NETWORK_REMOVED:
380 eventExecutor.execute(() -> processNetworkRemoval(event));
381 break;
382 default:
383 break;
384 }
385 }
386
387 private void processNetworkCreation(K8sNetworkEvent event) {
388 if (!isRelevantHelper()) {
389 return;
390 }
391
392 setGatewayRule(event.subject(), true);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900393 setLocalBridgeRules(event.subject(), true);
394 setLocalBridgeArpRules(event.subject(), true);
Jian Lic9d0d702020-10-07 11:50:58 +0900395 setInterNodeRoutingRules(event.subject(), true);
Jian Li7d111d72019-04-12 13:58:44 +0900396 }
397
398 private void processNetworkRemoval(K8sNetworkEvent event) {
399 if (!isRelevantHelper()) {
400 return;
401 }
402
403 setGatewayRule(event.subject(), false);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900404 setLocalBridgeRules(event.subject(), false);
405 setLocalBridgeArpRules(event.subject(), false);
Jian Lic9d0d702020-10-07 11:50:58 +0900406 setInterNodeRoutingRules(event.subject(), false);
Jian Li7d111d72019-04-12 13:58:44 +0900407 }
408 }
409
410 private class InternalK8sNodeListener implements K8sNodeListener {
411 private boolean isRelevantHelper() {
412 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
413 }
414
415 @Override
416 public void event(K8sNodeEvent event) {
417 switch (event.type()) {
418 case K8S_NODE_COMPLETE:
419 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
420 break;
421 case K8S_NODE_INCOMPLETE:
422 default:
423 break;
424 }
425 }
426
427 private void processNodeCompletion(K8sNode node) {
428 log.info("COMPLETE node {} is detected", node.hostname());
429
430 if (!isRelevantHelper()) {
431 return;
432 }
433
434 k8sNetworkService.networks().forEach(n -> setGatewayRule(n, true));
Jian Li1a2eb5d2019-08-27 02:07:05 +0900435 k8sNetworkService.networks().forEach(n -> setLocalBridgeRules(n, true));
436 k8sNetworkService.networks().forEach(n -> setLocalBridgeArpRules(n, true));
Jian Lic9d0d702020-10-07 11:50:58 +0900437 k8sNetworkService.networks().forEach(n -> setInterNodeRoutingRules(n, true));
Jian Li7d111d72019-04-12 13:58:44 +0900438 }
439 }
440}