blob: b77fb040d7e9826ccebdfe37a28c5f051863d992 [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;
40import org.onosproject.kubevirtnode.api.KubevirtNodeService;
41import org.onosproject.net.Device;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.device.DeviceAdminService;
44import org.onosproject.net.driver.DriverService;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
Daniel Park157947f2021-04-09 17:50:53 +090049import org.onosproject.net.packet.DefaultOutboundPacket;
50import org.onosproject.net.packet.PacketService;
Daniel Parkf3136042021-03-10 07:49:11 +090051import org.osgi.service.component.annotations.Activate;
52import org.osgi.service.component.annotations.Component;
53import org.osgi.service.component.annotations.Deactivate;
54import org.osgi.service.component.annotations.Reference;
55import org.osgi.service.component.annotations.ReferenceCardinality;
56import org.slf4j.Logger;
57
Daniel Park157947f2021-04-09 17:50:53 +090058import java.nio.ByteBuffer;
Daniel Parkf3136042021-03-10 07:49:11 +090059import java.util.Objects;
60import java.util.concurrent.ExecutorService;
61
62import static java.util.concurrent.Executors.newSingleThreadExecutor;
63import static org.onlab.util.Tools.groupedThreads;
64import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
65import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
66import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
67import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
68import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FLOATING_IP_RULE;
69import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FORWARDING_RULE;
70import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_DEFAULT_TABLE;
71import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GENEVE;
72import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GRE;
73import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VXLAN;
Daniel Park157947f2021-04-09 17:50:53 +090074import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.buildGarpPacket;
Daniel Parkf3136042021-03-10 07:49:11 +090075import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.externalPatchPortNum;
76import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
77import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
78import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelPort;
79import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildExtension;
80import static org.slf4j.LoggerFactory.getLogger;
81
82/**
83 * Handles kubevirt floating ip.
84 */
85@Component(immediate = true)
86public class KubevirtFloatingIpHandler {
87 protected final Logger log = getLogger(getClass());
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 protected CoreService coreService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 protected ClusterService clusterService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected LeadershipService leadershipService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 protected DeviceAdminService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park157947f2021-04-09 17:50:53 +0900102 protected PacketService packetService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Parkf3136042021-03-10 07:49:11 +0900105 protected KubevirtPortService kubevirtPortService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected KubevirtNodeService kubevirtNodeService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected KubevirtNetworkService kubevirtNetworkService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected KubevirtFlowRuleService flowService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected DriverService driverService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected KubevirtRouterService kubevirtRouterService;
121
122 private final ExecutorService eventExecutor = newSingleThreadExecutor(
123 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
124
125 private ApplicationId appId;
126 private NodeId localNodeId;
127
Jian Li517597a2021-03-22 11:04:52 +0900128 private final InternalRouterEventListener kubevirtRouterListener =
Daniel Parkf3136042021-03-10 07:49:11 +0900129 new InternalRouterEventListener();
130
131 @Activate
132 protected void activate() {
133 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
134 localNodeId = clusterService.getLocalNode().id();
135 leadershipService.runForLeadership(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900136 kubevirtRouterService.addListener(kubevirtRouterListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900137
138 log.info("Started");
139 }
140
141 @Deactivate
142 protected void deactivate() {
143 leadershipService.withdraw(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900144 kubevirtRouterService.removeListener(kubevirtRouterListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900145
146 eventExecutor.shutdown();
147
148 log.info("Stopped");
149 }
150
Daniel Park157947f2021-04-09 17:50:53 +0900151 private void setFloatingIpRulesForFip(KubevirtRouter router,
152 KubevirtFloatingIp floatingIp,
153 KubevirtNode electedGw,
154 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900155
156 KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
157 if (kubevirtPort == null) {
Daniel Parkf3136042021-03-10 07:49:11 +0900158 return;
159 }
160
161 KubevirtNetwork kubevirtNetwork = kubevirtNetworkService.network(kubevirtPort.networkId());
162 if (kubevirtNetwork.type() == VXLAN || kubevirtNetwork.type() == GENEVE || kubevirtNetwork.type() == GRE) {
163 setFloatingIpDownstreamRulesToGatewayTunBridge(router, floatingIp, kubevirtNetwork, kubevirtPort, install);
164 }
165
Daniel Park157947f2021-04-09 17:50:53 +0900166 setFloatingIpArpResponseRules(router, floatingIp, kubevirtPort, electedGw, install);
167 setFloatingIpUpstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
168 setFloatingIpDownstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
Daniel Parkf3136042021-03-10 07:49:11 +0900169 }
170
171 private void setFloatingIpArpResponseRules(KubevirtRouter router,
172 KubevirtFloatingIp floatingIp,
173 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900174 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900175 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900176 TrafficSelector selector = DefaultTrafficSelector.builder()
177 .matchInPort(externalPatchPortNum(deviceService, electedGw))
178 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
179 .matchArpOp(ARP.OP_REQUEST)
180 .matchArpTpa(floatingIp.floatingIp().getIp4Address())
181 .build();
182
183 Device device = deviceService.getDevice(electedGw.intgBridge());
184
185 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
186 .extension(RulePopulatorUtil.buildMoveEthSrcToDstExtension(device), device.id())
187 .extension(RulePopulatorUtil.buildMoveArpShaToThaExtension(device), device.id())
188 .extension(RulePopulatorUtil.buildMoveArpSpaToTpaExtension(device), device.id())
189 .setArpOp(ARP.OP_REPLY)
190 .setEthSrc(port.macAddress())
191 .setArpSha(port.macAddress())
192 .setArpSpa(floatingIp.floatingIp().getIp4Address())
193 .setOutput(PortNumber.IN_PORT)
194 .build();
195
196 flowService.setRule(
197 appId,
198 electedGw.intgBridge(),
199 selector,
200 treatment,
201 PRIORITY_ARP_GATEWAY_RULE,
202 PRE_FLAT_TABLE,
203 install);
204 }
Jian Li517597a2021-03-22 11:04:52 +0900205
Daniel Parkf3136042021-03-10 07:49:11 +0900206 private KubevirtPort getKubevirtPort(KubevirtFloatingIp floatingIp) {
207
208 return kubevirtPortService.ports().stream()
209 .filter(port -> port.ipAddress().equals(floatingIp.fixedIp()))
210 .findAny().orElse(null);
211 }
212
213 private void setFloatingIpUpstreamRules(KubevirtRouter router,
214 KubevirtFloatingIp floatingIp,
215 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900216 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900217 boolean install) {
218
Daniel Parkf3136042021-03-10 07:49:11 +0900219 MacAddress peerMacAddress = router.peerRouter().macAddress();
220
221 if (peerMacAddress == null) {
222 log.warn("Failed to install floating Ip rules for floating ip {} and router {}" +
223 "because there's no peer router mac address", floatingIp.floatingIp(),
224 router.name());
225 return;
226 }
227
228 MacAddress routerMacAddress = getRouterMacAddress(router);
229
230 TrafficSelector selector = DefaultTrafficSelector.builder()
231 .matchEthType(Ethernet.TYPE_IPV4)
232 .matchEthSrc(port.macAddress())
233 .matchEthDst(routerMacAddress)
234 .matchIPSrc(IpPrefix.valueOf(floatingIp.fixedIp(), 32))
235 .build();
236
237
238 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
239 .setEthDst(peerMacAddress)
240 .setEthSrc(port.macAddress())
241 .setIpSrc(floatingIp.floatingIp())
242 .setOutput(externalPatchPortNum(deviceService, electedGw))
243 .build();
244
245 flowService.setRule(
246 appId,
247 electedGw.intgBridge(),
248 selector,
249 treatment,
250 PRIORITY_FLOATING_IP_RULE,
251 PRE_FLAT_TABLE,
252 install);
253 }
254
255 private void setFloatingIpDownstreamRules(KubevirtRouter router,
256 KubevirtFloatingIp floatingIp,
257 KubevirtPort port,
Daniel Park157947f2021-04-09 17:50:53 +0900258 KubevirtNode electedGw,
Daniel Parkf3136042021-03-10 07:49:11 +0900259 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900260 MacAddress routerMacAddress = getRouterMacAddress(router);
261
262 TrafficSelector selector = DefaultTrafficSelector.builder()
263 .matchEthType(Ethernet.TYPE_IPV4)
264 .matchEthDst(port.macAddress())
265 .matchIPDst(IpPrefix.valueOf(floatingIp.floatingIp(), 32))
266 .build();
267
268 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
269 .setEthSrc(routerMacAddress)
270 .setEthDst(port.macAddress())
271 .setIpDst(floatingIp.fixedIp())
272 .transition(FORWARDING_TABLE)
273 .build();
274
275 flowService.setRule(
276 appId,
277 electedGw.intgBridge(),
278 selector,
279 treatment,
280 PRIORITY_FLOATING_IP_RULE,
281 PRE_FLAT_TABLE,
282 install);
283 }
284
Daniel Parkf3136042021-03-10 07:49:11 +0900285 private void setFloatingIpDownstreamRulesToGatewayTunBridge(KubevirtRouter router,
286 KubevirtFloatingIp floatingIp,
287 KubevirtNetwork network,
288 KubevirtPort port,
289 boolean install) {
290 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
291
292 if (electedGw == null) {
293 log.warn("Failed to install floating Ip rules for floating ip {} " +
294 "because there's no gateway assigned to it", floatingIp.floatingIp());
295 return;
296 }
297
298 KubevirtNode workerNode = kubevirtNodeService.node(port.deviceId());
299 if (workerNode == null) {
300 log.warn("Failed to install floating Ip rules for floating ip {} " +
301 "because fail to fine the worker node that the associated port is running on",
302 floatingIp.floatingIp());
Jian Li9793ec42021-03-19 15:03:32 +0900303 return;
Daniel Parkf3136042021-03-10 07:49:11 +0900304 }
305
306 PortNumber tunnelPortNumber = tunnelPort(electedGw, network);
307 if (tunnelPortNumber == null) {
308 return;
309 }
310
Daniel Parkf3136042021-03-10 07:49:11 +0900311 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
312 .matchEthType(Ethernet.TYPE_IPV4)
313 .matchIPDst(IpPrefix.valueOf(port.ipAddress(), 32))
314 .matchEthDst(port.macAddress());
315
316 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
317 .setTunnelId(Long.parseLong(network.segmentId()))
318 .extension(buildExtension(
319 deviceService,
320 electedGw.tunBridge(),
321 workerNode.dataIp().getIp4Address()),
322 electedGw.tunBridge())
323 .setOutput(tunnelPortNumber);
324
325 flowService.setRule(
326 appId,
327 electedGw.tunBridge(),
328 sBuilder.build(),
329 tBuilder.build(),
330 PRIORITY_FORWARDING_RULE,
331 TUNNEL_DEFAULT_TABLE,
332 install);
333 }
334
Daniel Park157947f2021-04-09 17:50:53 +0900335 private void processGarpPacketForFloatingIp(KubevirtFloatingIp floatingIp, KubevirtNode electedGw) {
336
337 if (floatingIp == null) {
338 return;
339 }
340
341 KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
342 if (kubevirtPort == null) {
343 log.warn("Failed to install floating Ip rules for floating ip {} " +
344 "because there's no kubevirt port associated to it", floatingIp.floatingIp());
345 return;
346 }
347
348 Ethernet ethernet = buildGarpPacket(kubevirtPort.macAddress(), floatingIp.floatingIp());
349 if (ethernet == null) {
350 return;
351 }
352
353 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
354 .setOutput(externalPatchPortNum(deviceService, electedGw)).build();
355
356 packetService.emit(new DefaultOutboundPacket(electedGw.intgBridge(), treatment,
357 ByteBuffer.wrap(ethernet.serialize())));
358 }
359
Daniel Parkf3136042021-03-10 07:49:11 +0900360 private class InternalRouterEventListener implements KubevirtRouterListener {
361 private boolean isRelevantHelper() {
362 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
363 }
364
Daniel Parkf3136042021-03-10 07:49:11 +0900365 @Override
366 public void event(KubevirtRouterEvent event) {
367 switch (event.type()) {
Daniel Parkf3136042021-03-10 07:49:11 +0900368 case KUBEVIRT_FLOATING_IP_ASSOCIATED:
369 eventExecutor.execute(() -> processFloatingIpAssociation(event.subject(),
370 event.floatingIp()));
371 break;
372 case KUBEVIRT_FLOATING_IP_DISASSOCIATED:
373 eventExecutor.execute(() -> processFloatingIpDisassociation(event.subject(),
374 event.floatingIp()));
375 break;
Daniel Park157947f2021-04-09 17:50:53 +0900376 case KUBEVIRT_GATEWAY_NODE_CHANGED:
377 eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
378 event.gateway()));
379 break;
Jian Li8f944d42021-03-23 00:43:29 +0900380 case KUBEVIRT_GATEWAY_NODE_ATTACHED:
381 eventExecutor.execute(() -> processGatewayNodeAttachment(event.subject(),
382 event.gateway()));
383 break;
384 case KUBEVIRT_GATEWAY_NODE_DETACHED:
385 eventExecutor.execute(() -> processGatewayNodeDetachment(event.subject(),
386 event.gateway()));
387 break;
Daniel Parkf3136042021-03-10 07:49:11 +0900388 default:
389 //do nothing
390 break;
391 }
392 }
393
Daniel Park157947f2021-04-09 17:50:53 +0900394 private void processRouterGatewayNodeChanged(KubevirtRouter router, String disAssociatedGateway) {
395
396 kubevirtRouterService.floatingIps()
397 .stream()
398 .filter(fip -> fip.routerName().equals(router.name())).forEach(fip -> {
399 KubevirtNode oldGw = kubevirtNodeService.node(disAssociatedGateway);
400 if (oldGw == null) {
401 return;
402 }
403
404 KubevirtNode newGw = kubevirtNodeService.node(router.electedGateway());
405 if (newGw == null) {
406 return;
407 }
408
409 setFloatingIpRulesForFip(router, fip, oldGw, false);
410
411 setFloatingIpRulesForFip(router, fip, newGw, true);
412 processGarpPacketForFloatingIp(fip, newGw);
413
414 });
415 }
416
Jian Li8f944d42021-03-23 00:43:29 +0900417 private void processGatewayNodeAttachment(KubevirtRouter router, String gatewayName) {
418 kubevirtRouterService.floatingIps().forEach(fip -> {
419 if (fip.routerName().equals(router.name())) {
420 KubevirtNode gw = kubevirtNodeService.node(gatewayName);
421 if (gw != null) {
422 setFloatingIpRulesForFip(router, fip, gw, true);
423 }
424 }
425 });
426 }
427
428 private void processGatewayNodeDetachment(KubevirtRouter router, String gatewayName) {
429 kubevirtRouterService.floatingIps().forEach(fip -> {
430 if (fip.routerName().equals(router.name())) {
431 KubevirtNode gw = kubevirtNodeService.node(gatewayName);
432 if (gw != null) {
433 setFloatingIpRulesForFip(router, fip, gw, false);
434 }
435 }
436 });
437 }
438
Daniel Parkf3136042021-03-10 07:49:11 +0900439 private void processFloatingIpAssociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
Daniel Park157947f2021-04-09 17:50:53 +0900440 if (!isRelevantHelper() || router.electedGateway() == null) {
Daniel Parkf3136042021-03-10 07:49:11 +0900441 return;
442 }
Daniel Park157947f2021-04-09 17:50:53 +0900443
444 KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
445
446 if (electedGw == null) {
447 return;
448 }
449
450 processGarpPacketForFloatingIp(floatingIp, electedGw);
451 setFloatingIpRulesForFip(router, floatingIp, electedGw, true);
Daniel Parkf3136042021-03-10 07:49:11 +0900452 }
453
454 private void processFloatingIpDisassociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
Daniel Park157947f2021-04-09 17:50:53 +0900455 if (!isRelevantHelper() || router.electedGateway() == null) {
Daniel Parkf3136042021-03-10 07:49:11 +0900456 return;
457 }
Daniel Park157947f2021-04-09 17:50:53 +0900458
459 KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
460
461 if (electedGw == null) {
462 return;
463 }
464 setFloatingIpRulesForFip(router, floatingIp, electedGw, false);
Daniel Parkf3136042021-03-10 07:49:11 +0900465 }
466 }
Daniel Parkf3136042021-03-10 07:49:11 +0900467}