blob: 808a32318bcf4a22e179560cd11a0879254c9d66 [file] [log] [blame]
Daniel Parkb9a22022021-03-04 18:58:47 +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.Ip4Address;
22import org.onlab.packet.IpAddress;
23import org.onlab.packet.IpPrefix;
24import org.onlab.packet.MacAddress;
25import org.onlab.packet.TpPort;
26import org.onosproject.cluster.ClusterService;
27import org.onosproject.cluster.LeadershipService;
28import org.onosproject.cluster.NodeId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
32import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
33import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
34import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
35import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
36import org.onosproject.kubevirtnetworking.api.KubevirtPort;
37import org.onosproject.kubevirtnetworking.api.KubevirtPortEvent;
38import org.onosproject.kubevirtnetworking.api.KubevirtPortListener;
39import org.onosproject.kubevirtnetworking.api.KubevirtPortService;
40import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
41import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
42import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
43import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
44import org.onosproject.kubevirtnetworking.util.RulePopulatorUtil;
45import org.onosproject.kubevirtnode.api.KubevirtNode;
46import org.onosproject.kubevirtnode.api.KubevirtNodeService;
47import org.onosproject.net.Device;
48import org.onosproject.net.Port;
49import org.onosproject.net.PortNumber;
50import org.onosproject.net.device.DeviceAdminService;
51import org.onosproject.net.driver.DriverService;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.flow.instructions.ExtensionTreatment;
57import org.osgi.service.component.annotations.Activate;
58import org.osgi.service.component.annotations.Component;
59import org.osgi.service.component.annotations.Deactivate;
60import org.osgi.service.component.annotations.Reference;
61import org.osgi.service.component.annotations.ReferenceCardinality;
62import org.slf4j.Logger;
63
64import java.util.Objects;
65import java.util.Set;
66import java.util.concurrent.ExecutorService;
67
68import static java.util.concurrent.Executors.newSingleThreadExecutor;
69import static org.onlab.util.Tools.groupedThreads;
70import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
71import static org.onosproject.kubevirtnetworking.api.Constants.FLAT_TABLE;
72import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
73import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
74import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
75import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
76import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
77import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
78import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterSnatIpAddress;
79import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getbrIntMacAddress;
80import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.CT_NAT_SRC_FLAG;
81import static org.onosproject.net.AnnotationKeys.PORT_NAME;
82import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Handles kubevirt routing snat.
86 */
87
88@Component(immediate = true)
89public class KubevirtRoutingSnatHandler {
90 protected final Logger log = getLogger(getClass());
91 private static final int DEFAULT_TTL = 0xff;
92
93 private static final int TP_PORT_MINIMUM_NUM = 1025;
94 private static final int TP_PORT_MAXIMUM_NUM = 65535;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected CoreService coreService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected ClusterService clusterService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected LeadershipService leadershipService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected DeviceAdminService deviceService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected KubevirtPortService kubevirtPortService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected KubevirtNodeService kubevirtNodeService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected KubevirtNetworkService kubevirtNetworkService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
118 protected KubevirtFlowRuleService flowService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
121 protected DriverService driverService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
124 protected KubevirtRouterService kubevirtRouterService;
125
126 private final ExecutorService eventExecutor = newSingleThreadExecutor(
127 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
128
129 private final InternalKubevirtPortListener kubevirtPortListener =
130 new InternalKubevirtPortListener();
131
132 private final InternalRouterEventListener kubevirtRouterlistener =
133 new InternalRouterEventListener();
134
135 private final InternalNetworkEventListener kubevirtNetworkEventListener =
136 new InternalNetworkEventListener();
137
138 private ApplicationId appId;
139 private NodeId localNodeId;
140
141 @Activate
142 protected void activate() {
143 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
144 localNodeId = clusterService.getLocalNode().id();
145 leadershipService.runForLeadership(appId.name());
146
147 kubevirtPortService.addListener(kubevirtPortListener);
148 kubevirtRouterService.addListener(kubevirtRouterlistener);
149 kubevirtNetworkService.addListener(kubevirtNetworkEventListener);
150
151 log.info("Started");
152 }
153
154 @Deactivate
155 protected void deactivate() {
156 leadershipService.withdraw(appId.name());
157 kubevirtPortService.removeListener(kubevirtPortListener);
158 kubevirtRouterService.removeListener(kubevirtRouterlistener);
159 kubevirtNetworkService.removeListener(kubevirtNetworkEventListener);
160
161 eventExecutor.shutdown();
162
163 log.info("Stopped");
164 }
165
166 private void initGatewayNodeSnatForRouter(KubevirtRouter router, boolean install) {
167 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
168
169 if (electedGw == null) {
170 log.warn("Fail to initialize gateway node snat for router {} " +
171 "there's no gateway assigned to it", router.name());
172 return;
173 }
174
175 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
176
177 if (routerSnatIp == null) {
178 log.warn("Fail to initialize gateway node snat for router {} " +
179 "there's no gateway snat ip assigned to it", router.name());
180 return;
181 }
182
183 setArpResponseToPeerRouter(electedGw, Ip4Address.valueOf(routerSnatIp), install);
184 setStatefulSnatUpstreamRules(electedGw, router, Ip4Address.valueOf(routerSnatIp), install);
185 setStatefulSnatDownstreamRuleForRouter(router, electedGw, Ip4Address.valueOf(routerSnatIp), install);
186 }
187
188 private void setArpResponseToPeerRouter(KubevirtNode gatewayNode, Ip4Address ip4Address, boolean install) {
189
190 TrafficSelector selector = DefaultTrafficSelector.builder()
191 .matchInPort(externalPatchPortNum(gatewayNode))
192 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
193 .matchArpOp(ARP.OP_REQUEST)
194 .matchArpTpa(ip4Address)
195 .build();
196
197 Device device = deviceService.getDevice(gatewayNode.intgBridge());
198
199 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
200 .extension(RulePopulatorUtil.buildMoveEthSrcToDstExtension(device), device.id())
201 .extension(RulePopulatorUtil.buildMoveArpShaToThaExtension(device), device.id())
202 .extension(RulePopulatorUtil.buildMoveArpSpaToTpaExtension(device), device.id())
203 .setArpOp(ARP.OP_REPLY)
204 .setEthSrc(DEFAULT_GATEWAY_MAC)
205 .setArpSha(DEFAULT_GATEWAY_MAC)
206 .setArpSpa(ip4Address)
207 .setOutput(PortNumber.IN_PORT)
208 .build();
209
210 flowService.setRule(
211 appId,
212 gatewayNode.intgBridge(),
213 selector,
214 treatment,
215 PRIORITY_ARP_GATEWAY_RULE,
216 PRE_FLAT_TABLE,
217 install);
218 }
219
220 private void setStatefulSnatUpstreamRules(KubevirtNode gatewayNode, KubevirtRouter router,
221 Ip4Address ip4Address, boolean install) {
222
223 MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
224
225 TrafficSelector selector = DefaultTrafficSelector.builder()
226 .matchEthType(Ethernet.TYPE_IPV4)
227 .matchEthDst(brIntMacAddress)
228 .build();
229
230 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
231
232 ExtensionTreatment natTreatment = RulePopulatorUtil
233 .niciraConnTrackTreatmentBuilder(driverService, gatewayNode.intgBridge())
234 .commit(true)
235 .natFlag(CT_NAT_SRC_FLAG)
236 .natAction(true)
237 .natIp(ip4Address)
238 .natPortMin(TpPort.tpPort(TP_PORT_MINIMUM_NUM))
239 .natPortMax(TpPort.tpPort(TP_PORT_MAXIMUM_NUM))
240 .build();
241
242 tBuilder.extension(natTreatment, gatewayNode.intgBridge())
243 .setEthDst(router.peerRouter().macAddress())
244 .setEthSrc(DEFAULT_GATEWAY_MAC)
245 .setOutput(externalPatchPortNum(gatewayNode));
246
247 flowService.setRule(
248 appId,
249 gatewayNode.intgBridge(),
250 selector,
251 tBuilder.build(),
252 PRIORITY_STATEFUL_SNAT_RULE,
253 PRE_FLAT_TABLE,
254 install);
255 }
256
257 private void setStatefulSnatDownStreamRuleForNetwork(KubevirtNode gatewayNode,
258 KubevirtRouter router,
259 KubevirtNetwork network,
260 boolean install) {
261 kubevirtPortService.ports(network.networkId()).forEach(kubevirtPort -> {
262 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
263 if (routerSnatIp == null) {
264 return;
265 }
266 setStatefulSnatDownStreamRuleForKubevirtPort(gatewayNode, IpAddress.valueOf(routerSnatIp),
267 kubevirtPort, install);
268 });
269 }
270
271 private void setStatefulSnatDownStreamRuleForKubevirtPort(KubevirtNode gatewayNode,
272 IpAddress gatewaySnatIp,
273 KubevirtPort kubevirtPort,
274 boolean install) {
275 MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
276
277 if (brIntMacAddress == null) {
278 log.error("Failed to set stateful snat downstream rule because " +
279 "there's no br-int port for device {}", gatewayNode.intgBridge());
280 return;
281 }
282
283 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
284 .matchEthType(Ethernet.TYPE_IPV4)
285 .matchIPDst(IpPrefix.valueOf(kubevirtPort.ipAddress(), 32));
286
287 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
288 .setEthDst(kubevirtPort.macAddress())
289 .transition(FORWARDING_TABLE)
290 .build();
291
292 flowService.setRule(
293 appId,
294 gatewayNode.intgBridge(),
295 sBuilder.build(),
296 treatment,
297 PRIORITY_STATEFUL_SNAT_RULE,
298 FLAT_TABLE,
299 install);
300 }
301
302 private void setStatefulSnatDownstreamRuleForRouter(KubevirtRouter router,
303 KubevirtNode gatewayNode,
304 IpAddress gatewaySnatIp,
305 boolean install) {
306
307 MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
308
309 if (brIntMacAddress == null) {
310 log.error("Failed to set stateful snat downstream rule because " +
311 "there's no br-int port for device {}", gatewayNode.intgBridge());
312 return;
313 }
314
315 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
316 .matchEthType(Ethernet.TYPE_IPV4)
317 .matchIPDst(IpPrefix.valueOf(gatewaySnatIp, 32));
318
319 ExtensionTreatment natTreatment = RulePopulatorUtil
320 .niciraConnTrackTreatmentBuilder(driverService, gatewayNode.intgBridge())
321 .commit(false)
322 .natAction(true)
323 .table((short) PRE_FLAT_TABLE)
324 .build();
325
326 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
327 .setEthSrc(brIntMacAddress)
328 .extension(natTreatment, gatewayNode.intgBridge())
329 .build();
330
331 flowService.setRule(
332 appId,
333 gatewayNode.intgBridge(),
334 sBuilder.build(),
335 treatment,
336 PRIORITY_STATEFUL_SNAT_RULE,
337 PRE_FLAT_TABLE,
338 install);
339
340 router.internal().forEach(networkName -> {
341 KubevirtNetwork network = kubevirtNetworkService.network(networkName);
342
343 if (network != null) {
344 setStatefulSnatDownStreamRuleForNetwork(gatewayNode, router, network, install);
345 }
346 });
347 }
348
349 private PortNumber externalPatchPortNum(KubevirtNode gatewayNode) {
350 Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
351 .filter(p -> p.isEnabled() &&
352 Objects.equals(p.annotations().value(PORT_NAME), "int-to-gateway"))
353 .findAny().orElse(null);
354
355 return port != null ? port.number() : null;
356 }
357
358 private class InternalRouterEventListener implements KubevirtRouterListener {
359 private boolean isRelevantHelper() {
360 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
361 }
362
363 @Override
364 public void event(KubevirtRouterEvent event) {
365 switch (event.type()) {
366 case KUBEVIRT_ROUTER_CREATED:
367 eventExecutor.execute(() -> processRouterCreation(event.subject()));
368 break;
369 case KUBEVIRT_ROUTER_REMOVED:
370 eventExecutor.execute(() -> processRouterDeletion(event.subject()));
371 break;
372 case KUBEVIRT_ROUTER_UPDATED:
373 eventExecutor.execute(() -> processRouterUpdate(event.subject()));
374 break;
375 case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_ATTACHED:
376 eventExecutor.execute(() -> processRouterInternalNetworksAttached(event.subject(),
377 event.internal()));
378 break;
379 case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_DETACHED:
380 eventExecutor.execute(() -> processRouterInternalNetworksDetached(event.subject(),
381 event.internal()));
382 break;
383 default:
384 //do nothing
385 break;
386 }
387 }
388 private void processRouterInternalNetworksAttached(KubevirtRouter router,
389 Set<String> attachedInternalNetworks) {
390 if (!isRelevantHelper()) {
391 return;
392 }
393
394 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
395 if (gwNode == null) {
396 return;
397 }
398
399 attachedInternalNetworks.forEach(networkId -> {
400 kubevirtPortService.ports(networkId).forEach(kubevirtPort -> {
401 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
402 if (routerSnatIp == null) {
403 return;
404 }
405 setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, IpAddress.valueOf(routerSnatIp),
406 kubevirtPort, true);
407 });
408 });
409 }
410
411 private void processRouterInternalNetworksDetached(KubevirtRouter router,
412 Set<String> detachedInternalNetworks) {
413 if (!isRelevantHelper()) {
414 return;
415 }
416
417 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
418 if (gwNode == null) {
419 return;
420 }
421
422 detachedInternalNetworks.forEach(networkId -> {
423 kubevirtPortService.ports(networkId).forEach(kubevirtPort -> {
424 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
425 if (routerSnatIp == null) {
426 log.info("snatIp is null");
427 return;
428 }
429 setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, IpAddress.valueOf(routerSnatIp),
430 kubevirtPort, false);
431 });
432 });
433 }
434 private void processRouterCreation(KubevirtRouter router) {
435 if (!isRelevantHelper()) {
436 return;
437 }
438 if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
439 initGatewayNodeSnatForRouter(router, true);
440 }
441 }
442
443 private void processRouterDeletion(KubevirtRouter router) {
444 if (!isRelevantHelper()) {
445 return;
446 }
447 if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
448 initGatewayNodeSnatForRouter(router, false);
449 }
450 }
451
452 private void processRouterUpdate(KubevirtRouter router) {
453 if (!isRelevantHelper()) {
454 return;
455 }
456 if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
457 initGatewayNodeSnatForRouter(router, true);
458 }
459 }
460 }
461
462 private class InternalNetworkEventListener implements KubevirtNetworkListener {
463
464 private boolean isRelevantHelper() {
465 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
466 }
467
468 @Override
469 public void event(KubevirtNetworkEvent event) {
470 switch (event.type()) {
471 case KUBEVIRT_NETWORK_CREATED:
472 eventExecutor.execute(() -> processNetworkCreation(event.subject()));
473 break;
474 case KUBEVIRT_NETWORK_REMOVED:
475 eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
476 break;
477 case KUBEVIRT_NETWORK_UPDATED:
478 default:
479 // do nothing
480 break;
481 }
482 }
483
484 private void processNetworkCreation(KubevirtNetwork network) {
485 if (!isRelevantHelper()) {
486 return;
487 }
488
489 switch (network.type()) {
490 case VXLAN:
491 case GRE:
492 case GENEVE:
493 break;
494 case FLAT:
495 case VLAN:
496 break;
497 default:
498 // do nothing
499 break;
500 }
501 }
502
503 private void processNetworkRemoval(KubevirtNetwork network) {
504 if (!isRelevantHelper()) {
505 return;
506 }
507
508 switch (network.type()) {
509 case VXLAN:
510 case GRE:
511 case GENEVE:
512 break;
513 case FLAT:
514 case VLAN:
515 break;
516 default:
517 // do nothing
518 break;
519 }
520 }
521 }
522
523 private class InternalKubevirtPortListener implements KubevirtPortListener {
524
525 private boolean isRelevantHelper() {
526 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
527 }
528
529 @Override
530 public void event(KubevirtPortEvent event) {
531 switch (event.type()) {
532 case KUBEVIRT_PORT_CREATED:
533 eventExecutor.execute(() -> processPortCreation(event.subject()));
534 break;
535 case KUBEVIRT_PORT_UPDATED:
536 eventExecutor.execute(() -> processPortUpdate(event.subject()));
537 break;
538 case KUBEVIRT_PORT_REMOVED:
539 eventExecutor.execute(() -> processPortDeletion(event.subject()));
540 break;
541 default:
542 //do nothing
543 break;
544 }
545 }
546
547 private void processPortCreation(KubevirtPort kubevirtPort) {
548 if (!isRelevantHelper()) {
549 return;
550 }
551
552 KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
553 if (router == null) {
554 return;
555 }
556
557 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
558
559 if (gwNode != null) {
560 IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
561 if (gatewaySnatIp == null) {
562 return;
563 }
564 setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, true);
565 }
566 }
567
568 private void processPortUpdate(KubevirtPort kubevirtPort) {
569 if (!isRelevantHelper()) {
570 return;
571 }
572
573 KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
574 if (router == null) {
575 return;
576 }
577
578 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
579
580 if (gwNode != null) {
581 IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
582 if (gatewaySnatIp == null) {
583 return;
584 }
585 setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, true);
586 }
587 }
588
589 private void processPortDeletion(KubevirtPort kubevirtPort) {
590 if (!isRelevantHelper()) {
591 return;
592 }
593
594 KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
595 if (router == null) {
596 return;
597 }
598
599 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
600
601 if (gwNode != null) {
602 IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
603 if (gatewaySnatIp == null) {
604 return;
605 }
606 setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, false);
607 }
608 }
609
610 private KubevirtRouter routerForKubevirtPort(KubevirtPort kubevirtPort) {
611 if (kubevirtPort.ipAddress() != null) {
612 return kubevirtRouterService.routers().stream()
613 .filter(r -> r.internal().contains(kubevirtPort.networkId()))
614 .findAny().orElse(null);
615 }
616
617 return null;
618 }
619 }
620}