blob: f1a5fe08b1dc9bb0f8e1c91351a7c1654a9adb96 [file] [log] [blame]
Daniel Parkf3136042021-03-10 07:49:11 +09001/*
2 * Copyright 2021-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.kubevirtnetworking.impl;
17
18import org.onlab.packet.ARP;
19import org.onlab.packet.EthType;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.IpPrefix;
22import org.onlab.packet.MacAddress;
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.kubevirtnetworking.api.KubevirtFloatingIp;
29import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
30import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
31import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
32import org.onosproject.kubevirtnetworking.api.KubevirtPort;
33import org.onosproject.kubevirtnetworking.api.KubevirtPortService;
34import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
35import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
36import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
37import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
38import org.onosproject.kubevirtnetworking.util.RulePopulatorUtil;
39import org.onosproject.kubevirtnode.api.KubevirtNode;
Jian Li517597a2021-03-22 11:04:52 +090040import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
41import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
Daniel Parkf3136042021-03-10 07:49:11 +090042import org.onosproject.kubevirtnode.api.KubevirtNodeService;
43import org.onosproject.net.Device;
44import org.onosproject.net.PortNumber;
45import org.onosproject.net.device.DeviceAdminService;
46import org.onosproject.net.driver.DriverService;
47import org.onosproject.net.flow.DefaultTrafficSelector;
48import org.onosproject.net.flow.DefaultTrafficTreatment;
49import org.onosproject.net.flow.TrafficSelector;
50import org.onosproject.net.flow.TrafficTreatment;
Daniel Park157947f2021-04-09 17:50:53 +090051import org.onosproject.net.packet.DefaultOutboundPacket;
52import org.onosproject.net.packet.PacketService;
Daniel Parkf3136042021-03-10 07:49:11 +090053import org.osgi.service.component.annotations.Activate;
54import org.osgi.service.component.annotations.Component;
55import org.osgi.service.component.annotations.Deactivate;
56import org.osgi.service.component.annotations.Reference;
57import org.osgi.service.component.annotations.ReferenceCardinality;
58import org.slf4j.Logger;
59
Daniel Park157947f2021-04-09 17:50:53 +090060import java.nio.ByteBuffer;
Daniel Parkf3136042021-03-10 07:49:11 +090061import java.util.Objects;
62import java.util.concurrent.ExecutorService;
63
64import static java.util.concurrent.Executors.newSingleThreadExecutor;
65import static org.onlab.util.Tools.groupedThreads;
66import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
67import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
68import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
69import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
70import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FLOATING_IP_RULE;
71import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FORWARDING_RULE;
72import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_DEFAULT_TABLE;
73import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GENEVE;
74import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GRE;
75import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VXLAN;
Daniel Park157947f2021-04-09 17:50:53 +090076import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.buildGarpPacket;
Daniel Parkf3136042021-03-10 07:49:11 +090077import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.externalPatchPortNum;
78import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
79import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
80import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelPort;
81import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildExtension;
82import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Handles kubevirt floating ip.
86 */
87@Component(immediate = true)
88public class KubevirtFloatingIpHandler {
89 protected final Logger log = getLogger(getClass());
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected CoreService coreService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected ClusterService clusterService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected LeadershipService leadershipService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected DeviceAdminService deviceService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park157947f2021-04-09 17:50:53 +0900104 protected PacketService packetService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkf3136042021-03-10 07:49:11 +0900107 protected KubevirtPortService kubevirtPortService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected KubevirtNodeService kubevirtNodeService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected KubevirtNetworkService kubevirtNetworkService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected KubevirtFlowRuleService flowService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
119 protected DriverService driverService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
122 protected KubevirtRouterService kubevirtRouterService;
123
124 private final ExecutorService eventExecutor = newSingleThreadExecutor(
125 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
126
127 private ApplicationId appId;
128 private NodeId localNodeId;
129
Jian Li517597a2021-03-22 11:04:52 +0900130 private final InternalRouterEventListener kubevirtRouterListener =
Daniel Parkf3136042021-03-10 07:49:11 +0900131 new InternalRouterEventListener();
132
Jian Li517597a2021-03-22 11:04:52 +0900133 private final InternalNodeEventListener kubevirtNodeListener =
134 new InternalNodeEventListener();
135
Daniel Parkf3136042021-03-10 07:49:11 +0900136 @Activate
137 protected void activate() {
138 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
139 localNodeId = clusterService.getLocalNode().id();
140 leadershipService.runForLeadership(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900141 kubevirtRouterService.addListener(kubevirtRouterListener);
142 kubevirtNodeService.addListener(kubevirtNodeListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900143
144 log.info("Started");
145 }
146
147 @Deactivate
148 protected void deactivate() {
149 leadershipService.withdraw(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900150 kubevirtRouterService.removeListener(kubevirtRouterListener);
151 kubevirtNodeService.removeListener(kubevirtNodeListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900152
153 eventExecutor.shutdown();
154
155 log.info("Stopped");
156 }
157
Daniel Park157947f2021-04-09 17:50:53 +0900158 private void setFloatingIpRulesForFip(KubevirtRouter router,
159 KubevirtFloatingIp floatingIp,
160 KubevirtNode electedGw,
161 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900162
163 KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
164 if (kubevirtPort == null) {
165 log.warn("Failed to install floating Ip rules for floating ip {} " +
166 "because there's no kubevirt port associated to it", floatingIp.floatingIp());
167 return;
168 }
169
170 KubevirtNetwork kubevirtNetwork = kubevirtNetworkService.network(kubevirtPort.networkId());
171 if (kubevirtNetwork.type() == VXLAN || kubevirtNetwork.type() == GENEVE || kubevirtNetwork.type() == GRE) {
172 setFloatingIpDownstreamRulesToGatewayTunBridge(router, floatingIp, kubevirtNetwork, kubevirtPort, install);
173 }
174
Daniel Park157947f2021-04-09 17:50:53 +0900175 setFloatingIpArpResponseRules(router, floatingIp, kubevirtPort, electedGw, install);
176 setFloatingIpUpstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
177 setFloatingIpDownstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
Daniel Parkf3136042021-03-10 07:49:11 +0900178 }
179
180 private void setFloatingIpArpResponseRules(KubevirtRouter router,
181 KubevirtFloatingIp floatingIp,
182 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900183 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900184 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900185 TrafficSelector selector = DefaultTrafficSelector.builder()
186 .matchInPort(externalPatchPortNum(deviceService, electedGw))
187 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
188 .matchArpOp(ARP.OP_REQUEST)
189 .matchArpTpa(floatingIp.floatingIp().getIp4Address())
190 .build();
191
192 Device device = deviceService.getDevice(electedGw.intgBridge());
193
194 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
195 .extension(RulePopulatorUtil.buildMoveEthSrcToDstExtension(device), device.id())
196 .extension(RulePopulatorUtil.buildMoveArpShaToThaExtension(device), device.id())
197 .extension(RulePopulatorUtil.buildMoveArpSpaToTpaExtension(device), device.id())
198 .setArpOp(ARP.OP_REPLY)
199 .setEthSrc(port.macAddress())
200 .setArpSha(port.macAddress())
201 .setArpSpa(floatingIp.floatingIp().getIp4Address())
202 .setOutput(PortNumber.IN_PORT)
203 .build();
204
205 flowService.setRule(
206 appId,
207 electedGw.intgBridge(),
208 selector,
209 treatment,
210 PRIORITY_ARP_GATEWAY_RULE,
211 PRE_FLAT_TABLE,
212 install);
213 }
Jian Li517597a2021-03-22 11:04:52 +0900214
Daniel Parkf3136042021-03-10 07:49:11 +0900215 private KubevirtPort getKubevirtPort(KubevirtFloatingIp floatingIp) {
216
217 return kubevirtPortService.ports().stream()
218 .filter(port -> port.ipAddress().equals(floatingIp.fixedIp()))
219 .findAny().orElse(null);
220 }
221
222 private void setFloatingIpUpstreamRules(KubevirtRouter router,
223 KubevirtFloatingIp floatingIp,
224 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900225 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900226 boolean install) {
227
Daniel Parkf3136042021-03-10 07:49:11 +0900228 MacAddress peerMacAddress = router.peerRouter().macAddress();
229
230 if (peerMacAddress == null) {
231 log.warn("Failed to install floating Ip rules for floating ip {} and router {}" +
232 "because there's no peer router mac address", floatingIp.floatingIp(),
233 router.name());
234 return;
235 }
236
237 MacAddress routerMacAddress = getRouterMacAddress(router);
238
239 TrafficSelector selector = DefaultTrafficSelector.builder()
240 .matchEthType(Ethernet.TYPE_IPV4)
241 .matchEthSrc(port.macAddress())
242 .matchEthDst(routerMacAddress)
243 .matchIPSrc(IpPrefix.valueOf(floatingIp.fixedIp(), 32))
244 .build();
245
246
247 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
248 .setEthDst(peerMacAddress)
249 .setEthSrc(port.macAddress())
250 .setIpSrc(floatingIp.floatingIp())
251 .setOutput(externalPatchPortNum(deviceService, electedGw))
252 .build();
253
254 flowService.setRule(
255 appId,
256 electedGw.intgBridge(),
257 selector,
258 treatment,
259 PRIORITY_FLOATING_IP_RULE,
260 PRE_FLAT_TABLE,
261 install);
262 }
263
264 private void setFloatingIpDownstreamRules(KubevirtRouter router,
265 KubevirtFloatingIp floatingIp,
266 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900267 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900268 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900269 MacAddress routerMacAddress = getRouterMacAddress(router);
270
271 TrafficSelector selector = DefaultTrafficSelector.builder()
272 .matchEthType(Ethernet.TYPE_IPV4)
273 .matchEthDst(port.macAddress())
274 .matchIPDst(IpPrefix.valueOf(floatingIp.floatingIp(), 32))
275 .build();
276
277 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
278 .setEthSrc(routerMacAddress)
279 .setEthDst(port.macAddress())
280 .setIpDst(floatingIp.fixedIp())
281 .transition(FORWARDING_TABLE)
282 .build();
283
284 flowService.setRule(
285 appId,
286 electedGw.intgBridge(),
287 selector,
288 treatment,
289 PRIORITY_FLOATING_IP_RULE,
290 PRE_FLAT_TABLE,
291 install);
292 }
293
Daniel Parkf3136042021-03-10 07:49:11 +0900294 private void setFloatingIpDownstreamRulesToGatewayTunBridge(KubevirtRouter router,
295 KubevirtFloatingIp floatingIp,
296 KubevirtNetwork network,
297 KubevirtPort port,
298 boolean install) {
299 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
300
301 if (electedGw == null) {
302 log.warn("Failed to install floating Ip rules for floating ip {} " +
303 "because there's no gateway assigned to it", floatingIp.floatingIp());
304 return;
305 }
306
307 KubevirtNode workerNode = kubevirtNodeService.node(port.deviceId());
308 if (workerNode == null) {
309 log.warn("Failed to install floating Ip rules for floating ip {} " +
310 "because fail to fine the worker node that the associated port is running on",
311 floatingIp.floatingIp());
Jian Li9793ec42021-03-19 15:03:32 +0900312 return;
Daniel Parkf3136042021-03-10 07:49:11 +0900313 }
314
315 PortNumber tunnelPortNumber = tunnelPort(electedGw, network);
316 if (tunnelPortNumber == null) {
317 return;
318 }
319
Daniel Parkf3136042021-03-10 07:49:11 +0900320 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
321 .matchEthType(Ethernet.TYPE_IPV4)
322 .matchIPDst(IpPrefix.valueOf(port.ipAddress(), 32))
323 .matchEthDst(port.macAddress());
324
325 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
326 .setTunnelId(Long.parseLong(network.segmentId()))
327 .extension(buildExtension(
328 deviceService,
329 electedGw.tunBridge(),
330 workerNode.dataIp().getIp4Address()),
331 electedGw.tunBridge())
332 .setOutput(tunnelPortNumber);
333
334 flowService.setRule(
335 appId,
336 electedGw.tunBridge(),
337 sBuilder.build(),
338 tBuilder.build(),
339 PRIORITY_FORWARDING_RULE,
340 TUNNEL_DEFAULT_TABLE,
341 install);
342 }
343
Daniel Park157947f2021-04-09 17:50:53 +0900344 private void processGarpPacketForFloatingIp(KubevirtFloatingIp floatingIp, KubevirtNode electedGw) {
345
346 if (floatingIp == null) {
347 return;
348 }
349
350 KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
351 if (kubevirtPort == null) {
352 log.warn("Failed to install floating Ip rules for floating ip {} " +
353 "because there's no kubevirt port associated to it", floatingIp.floatingIp());
354 return;
355 }
356
357 Ethernet ethernet = buildGarpPacket(kubevirtPort.macAddress(), floatingIp.floatingIp());
358 if (ethernet == null) {
359 return;
360 }
361
362 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
363 .setOutput(externalPatchPortNum(deviceService, electedGw)).build();
364
365 packetService.emit(new DefaultOutboundPacket(electedGw.intgBridge(), treatment,
366 ByteBuffer.wrap(ethernet.serialize())));
367 }
368
Daniel Parkf3136042021-03-10 07:49:11 +0900369 private class InternalRouterEventListener implements KubevirtRouterListener {
370 private boolean isRelevantHelper() {
371 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
372 }
373
Daniel Parkf3136042021-03-10 07:49:11 +0900374 @Override
375 public void event(KubevirtRouterEvent event) {
376 switch (event.type()) {
Daniel Parkf3136042021-03-10 07:49:11 +0900377 case KUBEVIRT_FLOATING_IP_ASSOCIATED:
378 eventExecutor.execute(() -> processFloatingIpAssociation(event.subject(),
379 event.floatingIp()));
380 break;
381 case KUBEVIRT_FLOATING_IP_DISASSOCIATED:
382 eventExecutor.execute(() -> processFloatingIpDisassociation(event.subject(),
383 event.floatingIp()));
384 break;
Daniel Park157947f2021-04-09 17:50:53 +0900385 case KUBEVIRT_GATEWAY_NODE_CHANGED:
386 eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
387 event.gateway()));
388 break;
Daniel Parkf3136042021-03-10 07:49:11 +0900389 default:
390 //do nothing
391 break;
392 }
393 }
394
Daniel Park157947f2021-04-09 17:50:53 +0900395 private void processRouterGatewayNodeChanged(KubevirtRouter router, String disAssociatedGateway) {
396
397 kubevirtRouterService.floatingIps()
398 .stream()
399 .filter(fip -> fip.routerName().equals(router.name())).forEach(fip -> {
400 KubevirtNode oldGw = kubevirtNodeService.node(disAssociatedGateway);
401 if (oldGw == null) {
402 return;
403 }
404
405 KubevirtNode newGw = kubevirtNodeService.node(router.electedGateway());
406 if (newGw == null) {
407 return;
408 }
409
410 setFloatingIpRulesForFip(router, fip, oldGw, false);
411
412 setFloatingIpRulesForFip(router, fip, newGw, true);
413 processGarpPacketForFloatingIp(fip, newGw);
414
415 });
416 }
417
Daniel Parkf3136042021-03-10 07:49:11 +0900418 private void processFloatingIpAssociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
Daniel Park157947f2021-04-09 17:50:53 +0900419 if (!isRelevantHelper() || router.electedGateway() == null) {
Daniel Parkf3136042021-03-10 07:49:11 +0900420 return;
421 }
Daniel Park157947f2021-04-09 17:50:53 +0900422
423 KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
424
425 if (electedGw == null) {
426 return;
427 }
428
429 processGarpPacketForFloatingIp(floatingIp, electedGw);
430 setFloatingIpRulesForFip(router, floatingIp, electedGw, true);
Daniel Parkf3136042021-03-10 07:49:11 +0900431 }
432
433 private void processFloatingIpDisassociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
Daniel Park157947f2021-04-09 17:50:53 +0900434 if (!isRelevantHelper() || router.electedGateway() == null) {
Daniel Parkf3136042021-03-10 07:49:11 +0900435 return;
436 }
Daniel Park157947f2021-04-09 17:50:53 +0900437
438 KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
439
440 if (electedGw == null) {
441 return;
442 }
443 setFloatingIpRulesForFip(router, floatingIp, electedGw, false);
Daniel Parkf3136042021-03-10 07:49:11 +0900444 }
445 }
Jian Li517597a2021-03-22 11:04:52 +0900446
447 private class InternalNodeEventListener implements KubevirtNodeListener {
448
449 private boolean isRelevantHelper() {
450 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
451 }
452
453 @Override
454 public void event(KubevirtNodeEvent event) {
455 switch (event.type()) {
456 case KUBEVIRT_NODE_COMPLETE:
457 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
458 break;
459 case KUBEVIRT_NODE_REMOVED:
460 case KUBEVIRT_NODE_INCOMPLETE:
461 default:
462 break;
463 }
464 }
465
466 private void processNodeCompletion(KubevirtNode node) {
Daniel Park157947f2021-04-09 17:50:53 +0900467 if (!isRelevantHelper() || !node.type().equals(KubevirtNode.Type.GATEWAY)) {
Jian Li517597a2021-03-22 11:04:52 +0900468 return;
469 }
470
471 kubevirtRouterService.floatingIps().forEach(fip -> {
472 KubevirtRouter router = kubevirtRouterService.router(fip.routerName());
Daniel Park157947f2021-04-09 17:50:53 +0900473
474 if (router != null && router.electedGateway() != null) {
475 KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
Jian Li517597a2021-03-22 11:04:52 +0900476 if (electedGw == null) {
477 return;
478 }
479
480 if (electedGw.hostname().equals(node.hostname())) {
Daniel Park157947f2021-04-09 17:50:53 +0900481 setFloatingIpRulesForFip(router, fip, electedGw, true);
Jian Li517597a2021-03-22 11:04:52 +0900482 }
483 }
484 });
485 }
486 }
Daniel Parkf3136042021-03-10 07:49:11 +0900487}