blob: 96d111c139260a371901033fce217257cde38370 [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;
51import 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
58import java.util.Objects;
59import java.util.concurrent.ExecutorService;
60
61import static java.util.concurrent.Executors.newSingleThreadExecutor;
62import static org.onlab.util.Tools.groupedThreads;
63import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
64import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
65import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
66import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
67import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FLOATING_IP_RULE;
68import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FORWARDING_RULE;
69import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_DEFAULT_TABLE;
70import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GENEVE;
71import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GRE;
72import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VXLAN;
73import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.externalPatchPortNum;
74import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
75import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
76import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelPort;
77import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildExtension;
78import static org.slf4j.LoggerFactory.getLogger;
79
80/**
81 * Handles kubevirt floating ip.
82 */
83@Component(immediate = true)
84public class KubevirtFloatingIpHandler {
85 protected final Logger log = getLogger(getClass());
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY)
91 protected ClusterService clusterService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected LeadershipService leadershipService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected DeviceAdminService deviceService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected KubevirtPortService kubevirtPortService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected KubevirtNodeService kubevirtNodeService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected KubevirtNetworkService kubevirtNetworkService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected KubevirtFlowRuleService flowService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected DriverService driverService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected KubevirtRouterService kubevirtRouterService;
116
117 private final ExecutorService eventExecutor = newSingleThreadExecutor(
118 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
119
120 private ApplicationId appId;
121 private NodeId localNodeId;
122
Jian Li517597a2021-03-22 11:04:52 +0900123 private final InternalRouterEventListener kubevirtRouterListener =
Daniel Parkf3136042021-03-10 07:49:11 +0900124 new InternalRouterEventListener();
125
Jian Li517597a2021-03-22 11:04:52 +0900126 private final InternalNodeEventListener kubevirtNodeListener =
127 new InternalNodeEventListener();
128
Daniel Parkf3136042021-03-10 07:49:11 +0900129 @Activate
130 protected void activate() {
131 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
132 localNodeId = clusterService.getLocalNode().id();
133 leadershipService.runForLeadership(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900134 kubevirtRouterService.addListener(kubevirtRouterListener);
135 kubevirtNodeService.addListener(kubevirtNodeListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900136
137 log.info("Started");
138 }
139
140 @Deactivate
141 protected void deactivate() {
142 leadershipService.withdraw(appId.name());
Jian Li517597a2021-03-22 11:04:52 +0900143 kubevirtRouterService.removeListener(kubevirtRouterListener);
144 kubevirtNodeService.removeListener(kubevirtNodeListener);
Daniel Parkf3136042021-03-10 07:49:11 +0900145
146 eventExecutor.shutdown();
147
148 log.info("Stopped");
149 }
150
151 private void setFloatingIpRules(KubevirtRouter router,
152 KubevirtFloatingIp floatingIp,
153 boolean install) {
154
155 KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
156 if (kubevirtPort == null) {
157 log.warn("Failed to install floating Ip rules for floating ip {} " +
158 "because there's no kubevirt port associated to it", floatingIp.floatingIp());
159 return;
160 }
161
162 KubevirtNetwork kubevirtNetwork = kubevirtNetworkService.network(kubevirtPort.networkId());
163 if (kubevirtNetwork.type() == VXLAN || kubevirtNetwork.type() == GENEVE || kubevirtNetwork.type() == GRE) {
164 setFloatingIpDownstreamRulesToGatewayTunBridge(router, floatingIp, kubevirtNetwork, kubevirtPort, install);
165 }
166
167 setFloatingIpArpResponseRules(router, floatingIp, kubevirtPort, install);
168 setFloatingIpUpstreamRules(router, floatingIp, kubevirtPort, install);
169 setFloatingIpDownstreamRules(router, floatingIp, kubevirtPort, install);
170 }
171
172 private void setFloatingIpArpResponseRules(KubevirtRouter router,
173 KubevirtFloatingIp floatingIp,
174 KubevirtPort port,
175 boolean install) {
176
177 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
178
179 if (electedGw == null) {
180 log.warn("Failed to install floating Ip rules for floating ip {} " +
181 "because there's no gateway assigned to it", floatingIp.floatingIp());
182 return;
183 }
184
185 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,
225 boolean install) {
226
227 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
228
229 if (electedGw == null) {
230 log.warn("Failed to install floating Ip rules for floating ip {} " +
231 "because there's no gateway assigned to it", floatingIp.floatingIp());
232 return;
233 }
234
235 MacAddress peerMacAddress = router.peerRouter().macAddress();
236
237 if (peerMacAddress == null) {
238 log.warn("Failed to install floating Ip rules for floating ip {} and router {}" +
239 "because there's no peer router mac address", floatingIp.floatingIp(),
240 router.name());
241 return;
242 }
243
244 MacAddress routerMacAddress = getRouterMacAddress(router);
245
246 TrafficSelector selector = DefaultTrafficSelector.builder()
247 .matchEthType(Ethernet.TYPE_IPV4)
248 .matchEthSrc(port.macAddress())
249 .matchEthDst(routerMacAddress)
250 .matchIPSrc(IpPrefix.valueOf(floatingIp.fixedIp(), 32))
251 .build();
252
253
254 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
255 .setEthDst(peerMacAddress)
256 .setEthSrc(port.macAddress())
257 .setIpSrc(floatingIp.floatingIp())
258 .setOutput(externalPatchPortNum(deviceService, electedGw))
259 .build();
260
261 flowService.setRule(
262 appId,
263 electedGw.intgBridge(),
264 selector,
265 treatment,
266 PRIORITY_FLOATING_IP_RULE,
267 PRE_FLAT_TABLE,
268 install);
269 }
270
271 private void setFloatingIpDownstreamRules(KubevirtRouter router,
272 KubevirtFloatingIp floatingIp,
273 KubevirtPort port,
274 boolean install) {
275 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
276
277 if (electedGw == null) {
278 log.warn("Failed to install floating Ip rules for floating ip {} " +
279 "because there's no gateway assigned to it", floatingIp.floatingIp());
280 return;
281 }
282
283 MacAddress routerMacAddress = getRouterMacAddress(router);
284
285 TrafficSelector selector = DefaultTrafficSelector.builder()
286 .matchEthType(Ethernet.TYPE_IPV4)
287 .matchEthDst(port.macAddress())
288 .matchIPDst(IpPrefix.valueOf(floatingIp.floatingIp(), 32))
289 .build();
290
291 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
292 .setEthSrc(routerMacAddress)
293 .setEthDst(port.macAddress())
294 .setIpDst(floatingIp.fixedIp())
295 .transition(FORWARDING_TABLE)
296 .build();
297
298 flowService.setRule(
299 appId,
300 electedGw.intgBridge(),
301 selector,
302 treatment,
303 PRIORITY_FLOATING_IP_RULE,
304 PRE_FLAT_TABLE,
305 install);
306 }
307
Daniel Parkf3136042021-03-10 07:49:11 +0900308 private void setFloatingIpDownstreamRulesToGatewayTunBridge(KubevirtRouter router,
309 KubevirtFloatingIp floatingIp,
310 KubevirtNetwork network,
311 KubevirtPort port,
312 boolean install) {
313 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
314
315 if (electedGw == null) {
316 log.warn("Failed to install floating Ip rules for floating ip {} " +
317 "because there's no gateway assigned to it", floatingIp.floatingIp());
318 return;
319 }
320
321 KubevirtNode workerNode = kubevirtNodeService.node(port.deviceId());
322 if (workerNode == null) {
323 log.warn("Failed to install floating Ip rules for floating ip {} " +
324 "because fail to fine the worker node that the associated port is running on",
325 floatingIp.floatingIp());
Jian Li9793ec42021-03-19 15:03:32 +0900326 return;
Daniel Parkf3136042021-03-10 07:49:11 +0900327 }
328
329 PortNumber tunnelPortNumber = tunnelPort(electedGw, network);
330 if (tunnelPortNumber == null) {
331 return;
332 }
333
Daniel Parkf3136042021-03-10 07:49:11 +0900334 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
335 .matchEthType(Ethernet.TYPE_IPV4)
336 .matchIPDst(IpPrefix.valueOf(port.ipAddress(), 32))
337 .matchEthDst(port.macAddress());
338
339 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
340 .setTunnelId(Long.parseLong(network.segmentId()))
341 .extension(buildExtension(
342 deviceService,
343 electedGw.tunBridge(),
344 workerNode.dataIp().getIp4Address()),
345 electedGw.tunBridge())
346 .setOutput(tunnelPortNumber);
347
348 flowService.setRule(
349 appId,
350 electedGw.tunBridge(),
351 sBuilder.build(),
352 tBuilder.build(),
353 PRIORITY_FORWARDING_RULE,
354 TUNNEL_DEFAULT_TABLE,
355 install);
356 }
357
358 private class InternalRouterEventListener implements KubevirtRouterListener {
359 private boolean isRelevantHelper() {
360 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
361 }
362
Daniel Parkf3136042021-03-10 07:49:11 +0900363 @Override
364 public void event(KubevirtRouterEvent event) {
365 switch (event.type()) {
Daniel Parkf3136042021-03-10 07:49:11 +0900366 case KUBEVIRT_FLOATING_IP_ASSOCIATED:
367 eventExecutor.execute(() -> processFloatingIpAssociation(event.subject(),
368 event.floatingIp()));
369 break;
370 case KUBEVIRT_FLOATING_IP_DISASSOCIATED:
371 eventExecutor.execute(() -> processFloatingIpDisassociation(event.subject(),
372 event.floatingIp()));
373 break;
374
375 default:
376 //do nothing
377 break;
378 }
379 }
380
Daniel Parkf3136042021-03-10 07:49:11 +0900381 private void processFloatingIpAssociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
382 if (!isRelevantHelper()) {
383 return;
384 }
385 setFloatingIpRules(router, floatingIp, true);
386 }
387
388 private void processFloatingIpDisassociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
389 if (!isRelevantHelper()) {
390 return;
391 }
392 setFloatingIpRules(router, floatingIp, false);
393 }
394 }
Jian Li517597a2021-03-22 11:04:52 +0900395
396 private class InternalNodeEventListener implements KubevirtNodeListener {
397
398 private boolean isRelevantHelper() {
399 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
400 }
401
402 @Override
403 public void event(KubevirtNodeEvent event) {
404 switch (event.type()) {
405 case KUBEVIRT_NODE_COMPLETE:
406 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
407 break;
408 case KUBEVIRT_NODE_REMOVED:
409 case KUBEVIRT_NODE_INCOMPLETE:
410 default:
411 break;
412 }
413 }
414
415 private void processNodeCompletion(KubevirtNode node) {
416 if (!isRelevantHelper()) {
417 return;
418 }
419
420 kubevirtRouterService.floatingIps().forEach(fip -> {
421 KubevirtRouter router = kubevirtRouterService.router(fip.routerName());
422 if (router != null) {
423 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
424 if (electedGw == null) {
425 return;
426 }
427
428 if (electedGw.hostname().equals(node.hostname())) {
429 setFloatingIpRules(router, fip, true);
430 }
431 }
432 });
433 }
434 }
Daniel Parkf3136042021-03-10 07:49:11 +0900435}