blob: 9ba10b4600283c6f76dabed26edb8c4d860e8e5c [file] [log] [blame]
Daniel Park5a3e9392021-03-23 08:00:00 +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.MacAddress;
24import org.onlab.packet.VlanId;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipService;
27import org.onosproject.cluster.NodeId;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
Daniel Park9955d582022-03-17 00:15:27 +090030
Daniel Park5a3e9392021-03-23 08:00:00 +090031import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
32import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
33import org.onosproject.kubevirtnetworking.api.KubevirtPeerRouter;
34import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
35import org.onosproject.kubevirtnetworking.api.KubevirtRouterAdminService;
36import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
37import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
38import org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil;
39import org.onosproject.kubevirtnode.api.KubevirtNode;
40import org.onosproject.kubevirtnode.api.KubevirtNodeService;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.packet.DefaultOutboundPacket;
48import org.onosproject.net.packet.InboundPacket;
49import org.onosproject.net.packet.PacketContext;
50import org.onosproject.net.packet.PacketProcessor;
51import org.onosproject.net.packet.PacketService;
52import org.osgi.service.component.annotations.Activate;
53import org.osgi.service.component.annotations.Component;
54import org.osgi.service.component.annotations.Deactivate;
55import org.osgi.service.component.annotations.Reference;
56import org.osgi.service.component.annotations.ReferenceCardinality;
57import org.slf4j.Logger;
58
59import java.nio.ByteBuffer;
60import java.util.Objects;
Daniel Park9955d582022-03-17 00:15:27 +090061import java.util.Timer;
62import java.util.TimerTask;
Daniel Park5a3e9392021-03-23 08:00:00 +090063import java.util.concurrent.ExecutorService;
64
65import static java.util.concurrent.Executors.newSingleThreadExecutor;
66import static org.onlab.util.Tools.groupedThreads;
67import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Lif89d9602021-04-27 19:05:49 +090068import static org.onosproject.kubevirtnetworking.api.Constants.GW_ENTRY_TABLE;
Daniel Park5a3e9392021-03-23 08:00:00 +090069import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
Daniel Park5a3e9392021-03-23 08:00:00 +090070import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
71import static org.slf4j.LoggerFactory.getLogger;
72
73/**
74 * Handles arp packet.
75 */
76@Component(immediate = true)
77public class KubevirtRoutingArpHandler {
78 protected final Logger log = getLogger(getClass());
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY)
81 protected CoreService coreService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY)
84 protected ClusterService clusterService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY)
87 protected LeadershipService leadershipService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 protected PacketService packetService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 protected DeviceService deviceService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected KubevirtRouterAdminService kubevirtRouterService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 protected KubevirtNetworkAdminService kubevirtNetworkService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
102 protected KubevirtNodeService kubevirtNodeService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected KubevirtFlowRuleService kubevirtFlowRuleService;
106
107 private final ExecutorService eventExecutor = newSingleThreadExecutor(
108 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
109
110 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
111
112 private final InternalRouterEventListener kubevirtRouterlistener = new InternalRouterEventListener();
113
Daniel Park9955d582022-03-17 00:15:27 +0900114 private final Timer timer = new Timer("kubevirtcni-routing-arphandler");
115 private static final long SECONDS = 1000L;
116
Daniel Park5a3e9392021-03-23 08:00:00 +0900117 private ApplicationId appId;
118 private NodeId localNodeId;
119
120
121 @Activate
122 protected void activate() {
123 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
124 localNodeId = clusterService.getLocalNode().id();
125 leadershipService.runForLeadership(appId.name());
126
127 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
128 kubevirtRouterService.addListener(kubevirtRouterlistener);
129
130 log.info("Started");
131 }
132
133 @Deactivate
134 protected void deactivate() {
135 leadershipService.withdraw(appId.name());
136 packetService.removeProcessor(packetProcessor);
137 kubevirtRouterService.removeListener(kubevirtRouterlistener);
138
139 eventExecutor.shutdown();
140
141 log.info("Stopped");
142 }
143 /**
144 * Triggers ARP request to retrieve the peer router mac address.
145 *
146 * @param router kubevirt router
147 * @param peerRouterIp peer router IP address
148 */
149 private void retrievePeerRouterMac(KubevirtRouter router, IpAddress peerRouterIp) {
150
151 log.info("Sending ARP request to the peer router {} to retrieve the MAC address.",
152 peerRouterIp.getIp4Address().toString());
153 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
154
155 if (routerSnatIp == null) {
156 return;
157 }
158
159 IpAddress sourceIp = IpAddress.valueOf(routerSnatIp);
160
161 MacAddress sourceMac = DEFAULT_GATEWAY_MAC;
162 Ethernet ethRequest = ARP.buildArpRequest(sourceMac.toBytes(),
163 sourceIp.toOctets(),
164 peerRouterIp.toOctets(), VlanId.NO_VID);
165
166 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
167
168 if (gatewayNode == null) {
169 return;
170 }
171
172 PortNumber externalPatchPortNum = KubevirtNetworkingUtil.externalPatchPortNum(deviceService, gatewayNode);
173
174 if (externalPatchPortNum == null) {
175 return;
176 }
177
178 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
179 .setOutput(externalPatchPortNum)
180 .build();
181
182 packetService.emit(new DefaultOutboundPacket(
183 gatewayNode.intgBridge(),
184 treatment,
185 ByteBuffer.wrap(ethRequest.serialize())));
186 }
187
188 /**
189 * Sets default ARP flow rule to retrieve peer router mac address.
190 *
191 * @param routerSnatIp route Snat IP
192 * @param peerRouterIp peer router IP
193 * @param gatewayNodeId gateway node
194 * @param install install if true, uninstall otherwise
195 */
196 private void setRuleArpRequestToController(IpAddress routerSnatIp,
197 IpAddress peerRouterIp,
198 String gatewayNodeId,
199 boolean install) {
200 KubevirtNode gatewayNode = kubevirtNodeService.node(gatewayNodeId);
201 if (gatewayNode == null) {
202 return;
203 }
204
205 if (routerSnatIp == null) {
206 return;
207 }
208
209 TrafficSelector selector = DefaultTrafficSelector.builder()
210 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
211 .matchArpOp(ARP.OP_REPLY)
212 .matchArpSpa(peerRouterIp.getIp4Address())
213 .matchArpTpa(routerSnatIp.getIp4Address())
214 .build();
215
216 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
217 .punt()
218 .build();
219
220 kubevirtFlowRuleService.setRule(
221 appId,
222 gatewayNode.intgBridge(),
223 selector,
224 treatment,
225 PRIORITY_ARP_GATEWAY_RULE,
Jian Lif89d9602021-04-27 19:05:49 +0900226 GW_ENTRY_TABLE,
Daniel Park5a3e9392021-03-23 08:00:00 +0900227 install
228 );
229 }
230
231 private class InternalRouterEventListener implements KubevirtRouterListener {
232 private boolean isRelevantHelper() {
233 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
234 }
235
236 @Override
237 public void event(KubevirtRouterEvent event) {
238 switch (event.type()) {
239 case KUBEVIRT_GATEWAY_NODE_ATTACHED:
240 case KUBEVIRT_ROUTER_EXTERNAL_NETWORK_ATTACHED:
241 eventExecutor.execute(() -> processRouterExternalNetAttachedOrGwAttached(event.subject()));
242 break;
Daniel Park8ad7c3b2021-04-09 15:45:59 +0900243 case KUBEVIRT_ROUTER_REMOVED:
Daniel Parka5ba88d2021-05-28 15:46:46 +0900244 eventExecutor.execute(() -> processRouterRemoved(event.subject()));
245 break;
Daniel Park5a3e9392021-03-23 08:00:00 +0900246 case KUBEVIRT_ROUTER_EXTERNAL_NETWORK_DETACHED:
Daniel Parka5ba88d2021-05-28 15:46:46 +0900247 eventExecutor.execute(() -> processRouterExternalNetDetached(event.subject(),
Daniel Park5a3e9392021-03-23 08:00:00 +0900248 event.externalIp(), event.externalPeerRouterIp()));
249 break;
250 case KUBEVIRT_GATEWAY_NODE_DETACHED:
251 eventExecutor.execute(() -> processRouterGatewayNodeDetached(event.subject(), event.gateway()));
252 break;
Daniel Park05a94582021-05-12 10:57:02 +0900253 case KUBEVIRT_GATEWAY_NODE_CHANGED:
254 eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
255 event.gateway()));
256 break;
Daniel Park5a3e9392021-03-23 08:00:00 +0900257 default:
258 //do nothing
259 break;
260 }
261 }
262
Daniel Park05a94582021-05-12 10:57:02 +0900263 private void processRouterGatewayNodeChanged(KubevirtRouter router, String oldGateway) {
264 if (!isRelevantHelper()) {
265 return;
266 }
267 processRouterGatewayNodeDetached(router, oldGateway);
268 processRouterExternalNetAttachedOrGwAttached(router);
269 }
270
Daniel Park5a3e9392021-03-23 08:00:00 +0900271 private void processRouterExternalNetAttachedOrGwAttached(KubevirtRouter router) {
272 if (!isRelevantHelper()) {
273 return;
274 }
275 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
276
277 if (gatewayNode == null) {
278 return;
279 }
280
281 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
282 if (routerSnatIp == null) {
283 return;
284 }
285
286 if (router.peerRouter() != null &&
287 router.peerRouter().macAddress() == null &&
288 router.peerRouter().ipAddress() != null) {
289 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
290 router.peerRouter().ipAddress(), gatewayNode.hostname(), true);
291
292 retrievePeerRouterMac(router, router.peerRouter().ipAddress());
Daniel Park9955d582022-03-17 00:15:27 +0900293 checkPeerRouterMacRetrieved(router);
294
Daniel Park5a3e9392021-03-23 08:00:00 +0900295 }
296 }
297
Daniel Park9955d582022-03-17 00:15:27 +0900298 private void checkPeerRouterMacRetrieved(KubevirtRouter router) {
299 InternalTimerTask task = new InternalTimerTask(router.name(), router.peerRouter().ipAddress());
300 timer.schedule(task, 5 * SECONDS, 60 * SECONDS);
301 }
302
Daniel Parka5ba88d2021-05-28 15:46:46 +0900303 private void processRouterExternalNetDetached(KubevirtRouter router, String routerSnatIp,
304 String peerRouterIp) {
305 log.info("processRouterRemovedOrExternalNetDetached called");
Daniel Park5a3e9392021-03-23 08:00:00 +0900306 if (!isRelevantHelper()) {
307 return;
308 }
309 if (router.electedGateway() == null) {
310 return;
311 }
312 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
313
314 if (gatewayNode == null) {
315 return;
316 }
317
318 if (routerSnatIp == null || peerRouterIp == null) {
319 return;
320 }
321 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
322 IpAddress.valueOf(peerRouterIp), gatewayNode.hostname(), false);
323 }
324
Daniel Parka5ba88d2021-05-28 15:46:46 +0900325
326 private void processRouterRemoved(KubevirtRouter router) {
327 if (!isRelevantHelper()) {
328 return;
329 }
330 if (router.electedGateway() == null) {
331 return;
332 }
333 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
334 if (gatewayNode == null) {
335 return;
336 }
337
338 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
339 if (routerSnatIp == null) {
340 return;
341 }
342
343 IpAddress peerRouterIp = router.peerRouter().ipAddress();
344 if (peerRouterIp == null) {
345 return;
346 }
347
348 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
349 peerRouterIp, gatewayNode.hostname(), false);
350 }
351
Daniel Park5a3e9392021-03-23 08:00:00 +0900352 private void processRouterGatewayNodeDetached(KubevirtRouter router, String detachedGatewayNode) {
353 if (!isRelevantHelper()) {
354 return;
355 }
Daniel Parka5ba88d2021-05-28 15:46:46 +0900356
Daniel Park5a3e9392021-03-23 08:00:00 +0900357 if (detachedGatewayNode == null) {
358 return;
359 }
Daniel Parka5ba88d2021-05-28 15:46:46 +0900360
Daniel Park5a3e9392021-03-23 08:00:00 +0900361 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
362 if (routerSnatIp == null) {
363 return;
364 }
365
366 if (router.peerRouter() != null && router.peerRouter().ipAddress() != null) {
367 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
368 router.peerRouter().ipAddress(), detachedGatewayNode, false);
369 }
370 }
371 }
372
373 private class InternalPacketProcessor implements PacketProcessor {
374 @Override
375 public void process(PacketContext context) {
376 if (context.isHandled()) {
377 return;
378 }
379
380 InboundPacket pkt = context.inPacket();
381 Ethernet ethernet = pkt.parsed();
382
383 if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) {
384 processArpPacket(ethernet);
385 }
386 }
387
388 private void processArpPacket(Ethernet ethernet) {
389 ARP arp = (ARP) ethernet.getPayload();
390
391 if (arp.getOpCode() == ARP.OP_REQUEST) {
392 return;
393 }
394
395 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
396 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
397
398 IpAddress tpa = Ip4Address.valueOf(arp.getTargetProtocolAddress());
399
400 KubevirtRouter router = kubevirtRouterService.routers().stream()
401 .filter(r -> r.peerRouter() != null && r.peerRouter().ipAddress().equals(spa))
402 .filter(r -> {
403 String routerSnatIp = r.external().keySet().stream().findAny().orElse(null);
404 if (routerSnatIp == null) {
405 return false;
406 }
407 return IpAddress.valueOf(routerSnatIp).equals(tpa);
408 })
409 .findAny().orElse(null);
410
411 if (router == null) {
412 return;
413 }
414
415 KubevirtPeerRouter peerRouter = new KubevirtPeerRouter(spa, sha);
416 log.info("Update peer router mac adress {} to router {}", peerRouter.macAddress(), router.name());
417
418 kubevirtRouterService.updatePeerRouterMac(router.name(), sha);
419 }
420 }
Daniel Park9955d582022-03-17 00:15:27 +0900421
422 private class InternalTimerTask extends TimerTask {
423 String routerName;
424 IpAddress routerIpAddress;
425
426 public InternalTimerTask(String routerName, IpAddress routerIpAddress) {
427 this.routerName = routerName;
428 this.routerIpAddress = routerIpAddress;
429 }
430
431 @Override
432 public void run() {
433 KubevirtRouter router = kubevirtRouterService.router(routerName);
434
435 if (router == null) {
436 return;
437 }
438
439 if (router.peerRouter().macAddress() != null) {
440 log.info("Peer Router Mac for {} is retrieved. Stop this task..", routerName);
441 this.cancel();
442 return;
443 }
444
445 retrievePeerRouterMac(router, routerIpAddress);
446 }
447 }
Daniel Park5a3e9392021-03-23 08:00:00 +0900448}