blob: 657203395e8e264a37d79229483976e40328897c [file] [log] [blame]
Jian Lib5ab63c2021-02-03 17:54:28 +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 com.google.common.collect.Lists;
Jian Li543fe852021-02-04 17:25:01 +090019import org.onlab.packet.ARP;
20import org.onlab.packet.EthType;
Jian Lib5ab63c2021-02-03 17:54:28 +090021import org.onlab.packet.Ethernet;
22import org.onlab.packet.IPv4;
Jian Li543fe852021-02-04 17:25:01 +090023import org.onlab.packet.Ip4Address;
Jian Lib5ab63c2021-02-03 17:54:28 +090024import org.onlab.packet.IpAddress;
Jian Li543fe852021-02-04 17:25:01 +090025import org.onlab.packet.IpPrefix;
Jian Lib5ab63c2021-02-03 17:54:28 +090026import org.onlab.packet.TpPort;
27import org.onlab.packet.UDP;
28import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.LeadershipService;
30import org.onosproject.cluster.NodeId;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
34import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
35import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
36import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
37import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
38import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
39import org.onosproject.kubevirtnode.api.KubevirtNode;
40import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
41import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
42import org.onosproject.kubevirtnode.api.KubevirtNodeService;
43import org.onosproject.net.Device;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.PortNumber;
46import org.onosproject.net.behaviour.BridgeConfig;
47import org.onosproject.net.behaviour.BridgeDescription;
48import org.onosproject.net.behaviour.BridgeName;
49import org.onosproject.net.behaviour.ControllerInfo;
50import org.onosproject.net.behaviour.DefaultBridgeDescription;
51import org.onosproject.net.behaviour.DefaultPatchDescription;
52import org.onosproject.net.behaviour.InterfaceConfig;
53import org.onosproject.net.behaviour.PatchDescription;
54import org.onosproject.net.device.DeviceAdminService;
55import org.onosproject.net.flow.DefaultTrafficSelector;
56import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.TrafficSelector;
58import org.onosproject.net.flow.TrafficTreatment;
59import org.osgi.service.component.annotations.Activate;
60import org.osgi.service.component.annotations.Component;
61import org.osgi.service.component.annotations.Deactivate;
62import org.osgi.service.component.annotations.Reference;
63import org.osgi.service.component.annotations.ReferenceCardinality;
64import org.slf4j.Logger;
65
66import java.util.List;
67import java.util.Objects;
68import java.util.concurrent.ExecutorService;
69
70import static java.lang.Thread.sleep;
71import static java.util.concurrent.Executors.newSingleThreadExecutor;
Jian Li543fe852021-02-04 17:25:01 +090072import static org.onlab.packet.ICMP.CODE_ECHO_REQEUST;
73import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
74import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
Jian Lib5ab63c2021-02-03 17:54:28 +090075import static org.onlab.util.Tools.groupedThreads;
Jian Li543fe852021-02-04 17:25:01 +090076import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Lib5ab63c2021-02-03 17:54:28 +090077import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
Jian Li543fe852021-02-04 17:25:01 +090078import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
Jian Lib5ab63c2021-02-03 17:54:28 +090079import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_DHCP_RULE;
Jian Li543fe852021-02-04 17:25:01 +090080import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FORWARDING_RULE;
81import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ICMP_RULE;
82import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ARP_TABLE;
Jian Lib5ab63c2021-02-03 17:54:28 +090083import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_DHCP_TABLE;
84import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_FORWARDING_TABLE;
Jian Li543fe852021-02-04 17:25:01 +090085import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ICMP_TABLE;
Jian Lib5ab63c2021-02-03 17:54:28 +090086import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_INBOUND_TABLE;
Jian Li543fe852021-02-04 17:25:01 +090087import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_TO_TUNNEL_PREFIX;
88import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
Jian Lib5ab63c2021-02-03 17:54:28 +090089import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.segmentIdHex;
Jian Li543fe852021-02-04 17:25:01 +090090import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_NX_IP_TTL;
91import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_OF_ICMP_TYPE;
92import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildLoadExtension;
93import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
94import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
95import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
96import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveIpSrcToDstExtension;
Jian Lib5ab63c2021-02-03 17:54:28 +090097import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_BRIDGE;
98import static org.slf4j.LoggerFactory.getLogger;
99
100/**
101 * Handles kubevirt network events.
102 */
103@Component(immediate = true)
104public class KubevirtNetworkHandler {
105 protected final Logger log = getLogger(getClass());
106 private static final String DEFAULT_OF_PROTO = "tcp";
107 private static final int DEFAULT_OFPORT = 6653;
108 private static final int DPID_BEGIN = 3;
109 private static final long SLEEP_MS = 3000; // we wait 3s for init each node
Jian Li543fe852021-02-04 17:25:01 +0900110 private static final int DEFAULT_TTL = 0xff;
Jian Lib5ab63c2021-02-03 17:54:28 +0900111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected CoreService coreService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected ClusterService clusterService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
119 protected LeadershipService leadershipService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
122 protected DeviceAdminService deviceService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
125 protected KubevirtApiConfigService apiConfigService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
128 protected KubevirtNodeService nodeService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
131 protected KubevirtNetworkService networkService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
134 protected KubevirtFlowRuleService flowService;
135
136 private final KubevirtNetworkListener networkListener = new InternalNetworkEventListener();
137 private final KubevirtNodeListener nodeListener = new InternalNodeEventListener();
138
139 private final ExecutorService eventExecutor = newSingleThreadExecutor(
140 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
141
142 private ApplicationId appId;
143 private NodeId localNodeId;
144
145 @Activate
146 protected void activate() {
147 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
148 localNodeId = clusterService.getLocalNode().id();
149 networkService.addListener(networkListener);
150 nodeService.addListener(nodeListener);
151 leadershipService.runForLeadership(appId.name());
152
153 log.info("Started");
154 }
155
156 @Deactivate
157 protected void deactivate() {
158 networkService.removeListener(networkListener);
159 nodeService.removeListener(nodeListener);
160 leadershipService.withdraw(appId.name());
161 eventExecutor.shutdown();
162
163 log.info("Stopped");
164 }
165
166 private void createBridge(KubevirtNode node, KubevirtNetwork network) {
167
168 Device tunBridge = deviceService.getDevice(network.tenantDeviceId(node.hostname()));
169 if (tunBridge != null) {
170 log.warn("The tunnel bridge {} already exists at node {}",
171 network.tenantBridgeName(), node.hostname());
172 setDefaultRules(node, network);
173 return;
174 }
175
176 Device device = deviceService.getDevice(node.ovsdb());
177
178 IpAddress serverIp = apiConfigService.apiConfig().ipAddress();
179 ControllerInfo controlInfo =
180 new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
181 List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
182
183 String dpid = network.tenantDeviceId(
184 node.hostname()).toString().substring(DPID_BEGIN);
185
186 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
187 .name(network.tenantBridgeName())
188 .failMode(BridgeDescription.FailMode.SECURE)
189 .datapathId(dpid)
190 .disableInBand()
191 .controllers(controllers);
192
193 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
194 bridgeConfig.addBridge(builder.build());
195 }
196
197 private void removeBridge(KubevirtNode node, KubevirtNetwork network) {
198 Device device = deviceService.getDevice(node.ovsdb());
199
200 BridgeName bridgeName = BridgeName.bridgeName(network.tenantBridgeName());
201
202 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
203 bridgeConfig.deleteBridge(bridgeName);
204 deviceService.removeDevice(network.tenantDeviceId(node.hostname()));
205 }
206
207 private void createPatchInterface(KubevirtNode node, KubevirtNetwork network) {
208 Device device = deviceService.getDevice(node.ovsdb());
209
210 if (device == null || !device.is(InterfaceConfig.class)) {
211 log.error("Failed to create patch interface on {}", node.ovsdb());
212 return;
213 }
214
215 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
216
Jian Li543fe852021-02-04 17:25:01 +0900217 String tenantToTunIntf =
218 TENANT_TO_TUNNEL_PREFIX + segmentIdHex(network.segmentId());
219 String tunToTenantIntf =
220 TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
Jian Lib5ab63c2021-02-03 17:54:28 +0900221
Jian Li543fe852021-02-04 17:25:01 +0900222 // tenant bridge -> tunnel bridge
223 PatchDescription brTenantTunPatchDesc =
Jian Lib5ab63c2021-02-03 17:54:28 +0900224 DefaultPatchDescription.builder()
225 .deviceId(network.tenantBridgeName())
Jian Li543fe852021-02-04 17:25:01 +0900226 .ifaceName(tenantToTunIntf)
227 .peer(tunToTenantIntf)
Jian Lib5ab63c2021-02-03 17:54:28 +0900228 .build();
229
Jian Li543fe852021-02-04 17:25:01 +0900230 ifaceConfig.addPatchMode(tenantToTunIntf, brTenantTunPatchDesc);
Jian Lib5ab63c2021-02-03 17:54:28 +0900231
Jian Li543fe852021-02-04 17:25:01 +0900232 // tunnel bridge -> tenant bridge
233 PatchDescription brTunTenantPatchDesc =
Jian Lib5ab63c2021-02-03 17:54:28 +0900234 DefaultPatchDescription.builder()
235 .deviceId(TUNNEL_BRIDGE)
Jian Li543fe852021-02-04 17:25:01 +0900236 .ifaceName(tunToTenantIntf)
237 .peer(tenantToTunIntf)
Jian Lib5ab63c2021-02-03 17:54:28 +0900238 .build();
Jian Li543fe852021-02-04 17:25:01 +0900239 ifaceConfig.addPatchMode(tunToTenantIntf, brTunTenantPatchDesc);
Jian Lib5ab63c2021-02-03 17:54:28 +0900240 }
241
242 private void removePatchInterface(KubevirtNode node, KubevirtNetwork network) {
243 Device device = deviceService.getDevice(node.ovsdb());
244
245 if (device == null || !device.is(InterfaceConfig.class)) {
246 log.error("Failed to create patch interface on {}", node.ovsdb());
247 return;
248 }
249
250 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
251
Jian Li543fe852021-02-04 17:25:01 +0900252 String tunToIntIntf = TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
Jian Lib5ab63c2021-02-03 17:54:28 +0900253
254 ifaceConfig.removePatchMode(tunToIntIntf);
255 }
256
257 private void setDefaultRules(KubevirtNode node, KubevirtNetwork network) {
258 DeviceId deviceId = network.tenantDeviceId(node.hostname());
259
260 while (!deviceService.isAvailable(deviceId)) {
261 log.warn("Device {} is not ready for installing rules", deviceId);
262
263 try {
264 sleep(SLEEP_MS);
265 } catch (InterruptedException e) {
266 log.error("Failed to check device availability", e);
267 }
268 }
269
270 flowService.connectTables(deviceId, TENANT_INBOUND_TABLE, TENANT_DHCP_TABLE);
Jian Li543fe852021-02-04 17:25:01 +0900271 flowService.connectTables(deviceId, TENANT_DHCP_TABLE, TENANT_ARP_TABLE);
272 flowService.connectTables(deviceId, TENANT_ARP_TABLE, TENANT_ICMP_TABLE);
273 flowService.connectTables(deviceId, TENANT_ICMP_TABLE, TENANT_FORWARDING_TABLE);
Jian Lib5ab63c2021-02-03 17:54:28 +0900274
275 setDhcpRule(deviceId, true);
276 setForwardingRule(deviceId, true);
Jian Li543fe852021-02-04 17:25:01 +0900277 setGatewayArpRule(node, network, true);
278 setGatewayIcmpRule(node, network, true);
Jian Lib5ab63c2021-02-03 17:54:28 +0900279
280 log.info("Install default flow rules for tenant bridge {}", network.tenantBridgeName());
281 }
282
283 private void setDhcpRule(DeviceId deviceId, boolean install) {
284 TrafficSelector selector = DefaultTrafficSelector.builder()
285 .matchEthType(Ethernet.TYPE_IPV4)
286 .matchIPProtocol(IPv4.PROTOCOL_UDP)
287 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
288 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
289 .build();
290
291 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
292 .punt()
293 .build();
294
295 flowService.setRule(
296 appId,
297 deviceId,
298 selector,
299 treatment,
300 PRIORITY_DHCP_RULE,
301 TENANT_DHCP_TABLE,
302 install);
303 }
304
Jian Li543fe852021-02-04 17:25:01 +0900305 private void setForwardingRule(DeviceId deviceId, boolean install) {
Jian Lib5ab63c2021-02-03 17:54:28 +0900306 TrafficSelector selector = DefaultTrafficSelector.builder().build();
307 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
308 .setOutput(PortNumber.NORMAL)
309 .build();
310
311 flowService.setRule(
312 appId,
313 deviceId,
314 selector,
315 treatment,
Jian Li543fe852021-02-04 17:25:01 +0900316 PRIORITY_FORWARDING_RULE,
Jian Lib5ab63c2021-02-03 17:54:28 +0900317 TENANT_FORWARDING_TABLE,
318 install);
319 }
320
Jian Li543fe852021-02-04 17:25:01 +0900321 private void setGatewayArpRule(KubevirtNode node, KubevirtNetwork network, boolean install) {
322 Device device = deviceService.getDevice(network.tenantDeviceId(node.hostname()));
323
324 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
325 sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
326 .matchArpOp(ARP.OP_REQUEST)
327 .matchArpTpa(Ip4Address.valueOf(network.gatewayIp().toString()));
328
329 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
330 tBuilder.extension(buildMoveEthSrcToDstExtension(device), device.id())
331 .extension(buildMoveArpShaToThaExtension(device), device.id())
332 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
333 .setArpOp(ARP.OP_REPLY)
334 .setArpSha(DEFAULT_GATEWAY_MAC)
335 .setArpSpa(Ip4Address.valueOf(network.gatewayIp().toString()))
336 .setEthSrc(DEFAULT_GATEWAY_MAC)
337 .setOutput(PortNumber.IN_PORT);
338
339 flowService.setRule(
340 appId,
341 device.id(),
342 sBuilder.build(),
343 tBuilder.build(),
344 PRIORITY_ARP_GATEWAY_RULE,
345 TENANT_ARP_TABLE,
346 install
347 );
348 }
349
350 private void setGatewayIcmpRule(KubevirtNode node, KubevirtNetwork network, boolean install) {
351 DeviceId deviceId = network.tenantDeviceId(node.hostname());
352 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
353 .matchEthType(Ethernet.TYPE_IPV4)
354 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
355 .matchIcmpType(TYPE_ECHO_REQUEST)
356 .matchIcmpCode(CODE_ECHO_REQEUST)
357 .matchIPDst(IpPrefix.valueOf(network.gatewayIp(), 32));
358
359 Device device = deviceService.getDevice(deviceId);
360 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
361 .extension(buildMoveEthSrcToDstExtension(device), device.id())
362 .extension(buildMoveIpSrcToDstExtension(device), device.id())
363 .extension(buildLoadExtension(device,
364 NXM_NX_IP_TTL, DEFAULT_TTL), device.id())
365 .extension(buildLoadExtension(device,
366 NXM_OF_ICMP_TYPE, TYPE_ECHO_REPLY), device.id())
367 .setIpSrc(network.gatewayIp())
368 .setEthSrc(DEFAULT_GATEWAY_MAC)
369 .setOutput(PortNumber.IN_PORT);
370
371 flowService.setRule(
372 appId,
373 deviceId,
374 sBuilder.build(),
375 tBuilder.build(),
376 PRIORITY_ICMP_RULE,
377 TENANT_ICMP_TABLE,
378 install);
379 }
380
Jian Lib5ab63c2021-02-03 17:54:28 +0900381 private class InternalNetworkEventListener implements KubevirtNetworkListener {
382
383 private boolean isRelevantHelper() {
384 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
385 }
386
387 @Override
388 public void event(KubevirtNetworkEvent event) {
389 switch (event.type()) {
390 case KUBEVIRT_NETWORK_CREATED:
391 eventExecutor.execute(() -> processNetworkCreation(event.subject()));
392 break;
393 case KUBEVIRT_NETWORK_REMOVED:
394 eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
395 break;
396 case KUBEVIRT_NETWORK_UPDATED:
397 default:
398 // do nothing
399 break;
400 }
401 }
402
403 private void processNetworkCreation(KubevirtNetwork network) {
404 if (!isRelevantHelper()) {
405 return;
406 }
407
408 switch (network.type()) {
409 case VXLAN:
410 case GRE:
411 case GENEVE:
412 initIntegrationTunnelBridge(network);
413 break;
414 case FLAT:
415 default:
416 // do nothing
417 break;
418 }
419 }
420
421 private void processNetworkRemoval(KubevirtNetwork network) {
422 if (!isRelevantHelper()) {
423 return;
424 }
425
426 switch (network.type()) {
427 case VXLAN:
428 case GRE:
429 case GENEVE:
430 purgeIntegrationTunnelBridge(network);
431 break;
432 case FLAT:
433 default:
434 // do nothing
435 break;
436 }
437 }
438
439 private void initIntegrationTunnelBridge(KubevirtNetwork network) {
440 if (network.segmentId() == null) {
441 return;
442 }
443
444 nodeService.completeNodes().forEach(n -> {
445 createBridge(n, network);
446 createPatchInterface(n, network);
447 setDefaultRules(n, network);
448 });
449 }
450
451 private void purgeIntegrationTunnelBridge(KubevirtNetwork network) {
452 if (network.segmentId() == null) {
453 return;
454 }
455
456 nodeService.completeNodes().forEach(n -> {
457 removePatchInterface(n, network);
458 removeBridge(n, network);
459 });
460 }
461 }
462
463 private class InternalNodeEventListener implements KubevirtNodeListener {
464
465 private boolean isRelevantHelper() {
466 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
467 }
468
469 @Override
470 public void event(KubevirtNodeEvent event) {
471 switch (event.type()) {
472 case KUBEVIRT_NODE_COMPLETE:
473 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
474 break;
475 case KUBEVIRT_NODE_INCOMPLETE:
476 case KUBEVIRT_NODE_UPDATED:
477 default:
478 // do nothing
479 break;
480 }
481 }
482
483 private void processNodeCompletion(KubevirtNode node) {
484 if (!isRelevantHelper()) {
485 return;
486 }
487
488 for (KubevirtNetwork network : networkService.networks()) {
489 switch (network.type()) {
490 case VXLAN:
491 case GRE:
492 case GENEVE:
493 if (network.segmentId() == null) {
494 continue;
495 }
496 createBridge(node, network);
497 createPatchInterface(node, network);
498 setDefaultRules(node, network);
499 break;
500 case FLAT:
501 default:
502 // do nothing
503 break;
504 }
505 }
506 }
507 }
508}