blob: a1c14159c1c13dc33591e758be58a19794979c11 [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;
30import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
31import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
32import org.onosproject.kubevirtnetworking.api.KubevirtPeerRouter;
33import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
34import org.onosproject.kubevirtnetworking.api.KubevirtRouterAdminService;
35import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
36import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
37import org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil;
38import org.onosproject.kubevirtnode.api.KubevirtNode;
39import org.onosproject.kubevirtnode.api.KubevirtNodeService;
40import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficSelector;
45import org.onosproject.net.flow.TrafficTreatment;
46import org.onosproject.net.packet.DefaultOutboundPacket;
47import org.onosproject.net.packet.InboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
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.nio.ByteBuffer;
59import 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.DEFAULT_GATEWAY_MAC;
Jian Lif89d9602021-04-27 19:05:49 +090065import static org.onosproject.kubevirtnetworking.api.Constants.GW_ENTRY_TABLE;
Daniel Park5a3e9392021-03-23 08:00:00 +090066import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
Daniel Park5a3e9392021-03-23 08:00:00 +090067import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
68import static org.slf4j.LoggerFactory.getLogger;
69
70/**
71 * Handles arp packet.
72 */
73@Component(immediate = true)
74public class KubevirtRoutingArpHandler {
75 protected final Logger log = getLogger(getClass());
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY)
78 protected CoreService coreService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY)
81 protected ClusterService clusterService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY)
84 protected LeadershipService leadershipService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY)
87 protected PacketService packetService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 protected DeviceService deviceService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 protected KubevirtRouterAdminService kubevirtRouterService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected KubevirtNetworkAdminService kubevirtNetworkService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 protected KubevirtNodeService kubevirtNodeService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
102 protected KubevirtFlowRuleService kubevirtFlowRuleService;
103
104 private final ExecutorService eventExecutor = newSingleThreadExecutor(
105 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
106
107 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
108
109 private final InternalRouterEventListener kubevirtRouterlistener = new InternalRouterEventListener();
110
111 private ApplicationId appId;
112 private NodeId localNodeId;
113
114
115 @Activate
116 protected void activate() {
117 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
118 localNodeId = clusterService.getLocalNode().id();
119 leadershipService.runForLeadership(appId.name());
120
121 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
122 kubevirtRouterService.addListener(kubevirtRouterlistener);
123
124 log.info("Started");
125 }
126
127 @Deactivate
128 protected void deactivate() {
129 leadershipService.withdraw(appId.name());
130 packetService.removeProcessor(packetProcessor);
131 kubevirtRouterService.removeListener(kubevirtRouterlistener);
132
133 eventExecutor.shutdown();
134
135 log.info("Stopped");
136 }
137 /**
138 * Triggers ARP request to retrieve the peer router mac address.
139 *
140 * @param router kubevirt router
141 * @param peerRouterIp peer router IP address
142 */
143 private void retrievePeerRouterMac(KubevirtRouter router, IpAddress peerRouterIp) {
144
145 log.info("Sending ARP request to the peer router {} to retrieve the MAC address.",
146 peerRouterIp.getIp4Address().toString());
147 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
148
149 if (routerSnatIp == null) {
150 return;
151 }
152
153 IpAddress sourceIp = IpAddress.valueOf(routerSnatIp);
154
155 MacAddress sourceMac = DEFAULT_GATEWAY_MAC;
156 Ethernet ethRequest = ARP.buildArpRequest(sourceMac.toBytes(),
157 sourceIp.toOctets(),
158 peerRouterIp.toOctets(), VlanId.NO_VID);
159
160 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
161
162 if (gatewayNode == null) {
163 return;
164 }
165
166 PortNumber externalPatchPortNum = KubevirtNetworkingUtil.externalPatchPortNum(deviceService, gatewayNode);
167
168 if (externalPatchPortNum == null) {
169 return;
170 }
171
172 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
173 .setOutput(externalPatchPortNum)
174 .build();
175
176 packetService.emit(new DefaultOutboundPacket(
177 gatewayNode.intgBridge(),
178 treatment,
179 ByteBuffer.wrap(ethRequest.serialize())));
180 }
181
182 /**
183 * Sets default ARP flow rule to retrieve peer router mac address.
184 *
185 * @param routerSnatIp route Snat IP
186 * @param peerRouterIp peer router IP
187 * @param gatewayNodeId gateway node
188 * @param install install if true, uninstall otherwise
189 */
190 private void setRuleArpRequestToController(IpAddress routerSnatIp,
191 IpAddress peerRouterIp,
192 String gatewayNodeId,
193 boolean install) {
194 KubevirtNode gatewayNode = kubevirtNodeService.node(gatewayNodeId);
195 if (gatewayNode == null) {
196 return;
197 }
198
199 if (routerSnatIp == null) {
200 return;
201 }
202
203 TrafficSelector selector = DefaultTrafficSelector.builder()
204 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
205 .matchArpOp(ARP.OP_REPLY)
206 .matchArpSpa(peerRouterIp.getIp4Address())
207 .matchArpTpa(routerSnatIp.getIp4Address())
208 .build();
209
210 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
211 .punt()
212 .build();
213
214 kubevirtFlowRuleService.setRule(
215 appId,
216 gatewayNode.intgBridge(),
217 selector,
218 treatment,
219 PRIORITY_ARP_GATEWAY_RULE,
Jian Lif89d9602021-04-27 19:05:49 +0900220 GW_ENTRY_TABLE,
Daniel Park5a3e9392021-03-23 08:00:00 +0900221 install
222 );
223 }
224
225 private class InternalRouterEventListener implements KubevirtRouterListener {
226 private boolean isRelevantHelper() {
227 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
228 }
229
230 @Override
231 public void event(KubevirtRouterEvent event) {
232 switch (event.type()) {
233 case KUBEVIRT_GATEWAY_NODE_ATTACHED:
234 case KUBEVIRT_ROUTER_EXTERNAL_NETWORK_ATTACHED:
235 eventExecutor.execute(() -> processRouterExternalNetAttachedOrGwAttached(event.subject()));
236 break;
Daniel Park8ad7c3b2021-04-09 15:45:59 +0900237 case KUBEVIRT_ROUTER_REMOVED:
Daniel Parka5ba88d2021-05-28 15:46:46 +0900238 eventExecutor.execute(() -> processRouterRemoved(event.subject()));
239 break;
Daniel Park5a3e9392021-03-23 08:00:00 +0900240 case KUBEVIRT_ROUTER_EXTERNAL_NETWORK_DETACHED:
Daniel Parka5ba88d2021-05-28 15:46:46 +0900241 eventExecutor.execute(() -> processRouterExternalNetDetached(event.subject(),
Daniel Park5a3e9392021-03-23 08:00:00 +0900242 event.externalIp(), event.externalPeerRouterIp()));
243 break;
244 case KUBEVIRT_GATEWAY_NODE_DETACHED:
245 eventExecutor.execute(() -> processRouterGatewayNodeDetached(event.subject(), event.gateway()));
246 break;
Daniel Park05a94582021-05-12 10:57:02 +0900247 case KUBEVIRT_GATEWAY_NODE_CHANGED:
248 eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
249 event.gateway()));
250 break;
Daniel Park5a3e9392021-03-23 08:00:00 +0900251 default:
252 //do nothing
253 break;
254 }
255 }
256
Daniel Park05a94582021-05-12 10:57:02 +0900257 private void processRouterGatewayNodeChanged(KubevirtRouter router, String oldGateway) {
258 if (!isRelevantHelper()) {
259 return;
260 }
261 processRouterGatewayNodeDetached(router, oldGateway);
262 processRouterExternalNetAttachedOrGwAttached(router);
263 }
264
Daniel Park5a3e9392021-03-23 08:00:00 +0900265 private void processRouterExternalNetAttachedOrGwAttached(KubevirtRouter router) {
266 if (!isRelevantHelper()) {
267 return;
268 }
269 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
270
271 if (gatewayNode == null) {
272 return;
273 }
274
275 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
276 if (routerSnatIp == null) {
277 return;
278 }
279
280 if (router.peerRouter() != null &&
281 router.peerRouter().macAddress() == null &&
282 router.peerRouter().ipAddress() != null) {
283 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
284 router.peerRouter().ipAddress(), gatewayNode.hostname(), true);
285
286 retrievePeerRouterMac(router, router.peerRouter().ipAddress());
287 }
288 }
289
Daniel Parka5ba88d2021-05-28 15:46:46 +0900290 private void processRouterExternalNetDetached(KubevirtRouter router, String routerSnatIp,
291 String peerRouterIp) {
292 log.info("processRouterRemovedOrExternalNetDetached called");
Daniel Park5a3e9392021-03-23 08:00:00 +0900293 if (!isRelevantHelper()) {
294 return;
295 }
296 if (router.electedGateway() == null) {
297 return;
298 }
299 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
300
301 if (gatewayNode == null) {
302 return;
303 }
304
305 if (routerSnatIp == null || peerRouterIp == null) {
306 return;
307 }
308 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
309 IpAddress.valueOf(peerRouterIp), gatewayNode.hostname(), false);
310 }
311
Daniel Parka5ba88d2021-05-28 15:46:46 +0900312
313 private void processRouterRemoved(KubevirtRouter router) {
314 if (!isRelevantHelper()) {
315 return;
316 }
317 if (router.electedGateway() == null) {
318 return;
319 }
320 KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
321 if (gatewayNode == null) {
322 return;
323 }
324
325 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
326 if (routerSnatIp == null) {
327 return;
328 }
329
330 IpAddress peerRouterIp = router.peerRouter().ipAddress();
331 if (peerRouterIp == null) {
332 return;
333 }
334
335 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
336 peerRouterIp, gatewayNode.hostname(), false);
337 }
338
Daniel Park5a3e9392021-03-23 08:00:00 +0900339 private void processRouterGatewayNodeDetached(KubevirtRouter router, String detachedGatewayNode) {
340 if (!isRelevantHelper()) {
341 return;
342 }
Daniel Parka5ba88d2021-05-28 15:46:46 +0900343
Daniel Park5a3e9392021-03-23 08:00:00 +0900344 if (detachedGatewayNode == null) {
345 return;
346 }
Daniel Parka5ba88d2021-05-28 15:46:46 +0900347
Daniel Park5a3e9392021-03-23 08:00:00 +0900348 String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
349 if (routerSnatIp == null) {
350 return;
351 }
352
353 if (router.peerRouter() != null && router.peerRouter().ipAddress() != null) {
354 setRuleArpRequestToController(IpAddress.valueOf(routerSnatIp),
355 router.peerRouter().ipAddress(), detachedGatewayNode, false);
356 }
357 }
358 }
359
360 private class InternalPacketProcessor implements PacketProcessor {
361 @Override
362 public void process(PacketContext context) {
363 if (context.isHandled()) {
364 return;
365 }
366
367 InboundPacket pkt = context.inPacket();
368 Ethernet ethernet = pkt.parsed();
369
370 if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) {
371 processArpPacket(ethernet);
372 }
373 }
374
375 private void processArpPacket(Ethernet ethernet) {
376 ARP arp = (ARP) ethernet.getPayload();
377
378 if (arp.getOpCode() == ARP.OP_REQUEST) {
379 return;
380 }
381
382 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
383 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
384
385 IpAddress tpa = Ip4Address.valueOf(arp.getTargetProtocolAddress());
386
387 KubevirtRouter router = kubevirtRouterService.routers().stream()
388 .filter(r -> r.peerRouter() != null && r.peerRouter().ipAddress().equals(spa))
389 .filter(r -> {
390 String routerSnatIp = r.external().keySet().stream().findAny().orElse(null);
391 if (routerSnatIp == null) {
392 return false;
393 }
394 return IpAddress.valueOf(routerSnatIp).equals(tpa);
395 })
396 .findAny().orElse(null);
397
398 if (router == null) {
399 return;
400 }
401
402 KubevirtPeerRouter peerRouter = new KubevirtPeerRouter(spa, sha);
403 log.info("Update peer router mac adress {} to router {}", peerRouter.macAddress(), router.name());
404
405 kubevirtRouterService.updatePeerRouterMac(router.name(), sha);
406 }
407 }
408}