blob: 01659750d046a33c3ffa09d75d56cf4e8969bcfe [file] [log] [blame]
Jian Li4aa17642019-01-30 00:01:11 +09001/*
2 * Copyright 2019-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.k8snetworking.impl;
17
18import com.google.common.base.Strings;
19import org.onlab.packet.Ethernet;
Jian Li2cc2b632019-02-18 00:56:40 +090020import org.onlab.packet.IpPrefix;
Jian Li4aa17642019-01-30 00:01:11 +090021import org.onosproject.cfg.ComponentConfigService;
22import org.onosproject.cfg.ConfigProperty;
Jian Lieb488ea2019-04-16 01:50:02 +090023import org.onosproject.cluster.ClusterService;
Jian Li4aa17642019-01-30 00:01:11 +090024import org.onosproject.cluster.LeadershipService;
Jian Lieb488ea2019-04-16 01:50:02 +090025import org.onosproject.cluster.NodeId;
Jian Li4aa17642019-01-30 00:01:11 +090026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.k8snetworking.api.K8sFlowRuleService;
29import org.onosproject.k8snetworking.api.K8sNetwork;
30import org.onosproject.k8snetworking.api.K8sNetworkEvent;
31import org.onosproject.k8snetworking.api.K8sNetworkListener;
32import org.onosproject.k8snetworking.api.K8sNetworkService;
33import org.onosproject.k8snetworking.api.K8sPort;
34import org.onosproject.k8snode.api.K8sNode;
Jian Lieb488ea2019-04-16 01:50:02 +090035import org.onosproject.k8snode.api.K8sNodeEvent;
36import org.onosproject.k8snode.api.K8sNodeListener;
Jian Li4aa17642019-01-30 00:01:11 +090037import org.onosproject.k8snode.api.K8sNodeService;
38import org.onosproject.mastership.MastershipService;
39import org.onosproject.net.PortNumber;
40import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.driver.DriverService;
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.osgi.service.component.annotations.Activate;
47import org.osgi.service.component.annotations.Component;
48import org.osgi.service.component.annotations.Deactivate;
49import org.osgi.service.component.annotations.Reference;
50import org.osgi.service.component.annotations.ReferenceCardinality;
51import org.slf4j.Logger;
52
Jian Lieb488ea2019-04-16 01:50:02 +090053import java.util.Objects;
Jian Li4aa17642019-01-30 00:01:11 +090054import java.util.Set;
55import java.util.concurrent.ExecutorService;
56
57import static java.util.concurrent.Executors.newSingleThreadExecutor;
58import static org.onlab.util.Tools.groupedThreads;
Jian Li4aa17642019-01-30 00:01:11 +090059import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
60import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
61import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
Jian Lie1a5b8f2019-07-23 17:13:19 +090062import static org.onosproject.k8snetworking.api.Constants.JUMP_TABLE;
Jian Li4aa17642019-01-30 00:01:11 +090063import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li619fa282020-09-02 14:45:35 +090064import static org.onosproject.k8snetworking.api.Constants.PRIORITY_DEFAULT_RULE;
Jian Li4aa17642019-01-30 00:01:11 +090065import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
66import static org.onosproject.k8snetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
Jian Li619fa282020-09-02 14:45:35 +090067import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
Jian Li4aa17642019-01-30 00:01:11 +090068import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
69import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
70import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
71import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
72import static org.slf4j.LoggerFactory.getLogger;
73
74/**
75 * Populates switching flow rules on OVS for the basic connectivity among the
76 * container in the same network.
77 */
78@Component(immediate = true)
79public class K8sSwitchingHandler {
80
81 private final Logger log = getLogger(getClass());
82
83 private static final String ARP_MODE = "arpMode";
84 private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " +
85 "%s: Failed to get VNI for %s";
86 private static final String STR_NONE = "<none>";
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY)
89 protected CoreService coreService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected MastershipService mastershipService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieb488ea2019-04-16 01:50:02 +090095 protected ClusterService clusterService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +090098 protected DeviceService deviceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected DriverService driverService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected ComponentConfigService configService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected LeadershipService leadershipService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected K8sFlowRuleService k8sFlowRuleService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected K8sNetworkService k8sNetworkService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected K8sNodeService k8sNodeService;
117
118 private final ExecutorService eventExecutor = newSingleThreadExecutor(
119 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
120 private final InternalK8sNetworkListener k8sNetworkListener =
121 new InternalK8sNetworkListener();
Jian Lieb488ea2019-04-16 01:50:02 +0900122 private final InternalK8sNodeListener k8sNodeListener =
123 new InternalK8sNodeListener();
Jian Li4aa17642019-01-30 00:01:11 +0900124
125 private ApplicationId appId;
Jian Lieb488ea2019-04-16 01:50:02 +0900126 private NodeId localNodeId;
Jian Li4aa17642019-01-30 00:01:11 +0900127
128 @Activate
129 protected void activate() {
130 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
131 k8sNetworkService.addListener(k8sNetworkListener);
Jian Lieb488ea2019-04-16 01:50:02 +0900132 localNodeId = clusterService.getLocalNode().id();
133 k8sNodeService.addListener(k8sNodeListener);
134 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900135
Jian Li2cc2b632019-02-18 00:56:40 +0900136 setGatewayRulesForTunnel(true);
137
Jian Li4aa17642019-01-30 00:01:11 +0900138 log.info("Started");
139 }
140
141 @Deactivate
142 protected void deactivate() {
Jian Lieb488ea2019-04-16 01:50:02 +0900143 leadershipService.withdraw(appId.name());
144 k8sNodeService.removeListener(k8sNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900145 k8sNetworkService.removeListener(k8sNetworkListener);
146 eventExecutor.shutdown();
147
Jian Li2cc2b632019-02-18 00:56:40 +0900148 setGatewayRulesForTunnel(false);
149
Jian Li4aa17642019-01-30 00:01:11 +0900150 log.info("Stopped");
151 }
152
153 /**
154 * Configures the flow rules which are used for L2 packet switching.
Jian Li619fa282020-09-02 14:45:35 +0900155 * Note that these rules will be inserted in switching table (table 80).
Jian Li4aa17642019-01-30 00:01:11 +0900156 *
157 * @param port kubernetes port object
158 * @param install install flag, add the rule if true, remove it otherwise
159 */
160 private void setForwardingRulesForTunnel(K8sPort port, boolean install) {
161 // switching rules for the instPorts in the same node
162 TrafficSelector selector = DefaultTrafficSelector.builder()
163 // TODO: need to handle IPv6 in near future
164 .matchEthType(Ethernet.TYPE_IPV4)
165 .matchIPDst(port.ipAddress().toIpPrefix())
Jian Li619fa282020-09-02 14:45:35 +0900166 // .matchTunnelId(getVni(port))
Jian Li4aa17642019-01-30 00:01:11 +0900167 .build();
168
169 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
170 .setEthDst(port.macAddress())
171 .setOutput(port.portNumber())
172 .build();
173
174 k8sFlowRuleService.setRule(
175 appId,
176 port.deviceId(),
177 selector,
178 treatment,
179 PRIORITY_SWITCHING_RULE,
180 FORWARDING_TABLE,
181 install);
182
183 // switching rules for the node in the remote node
184 K8sNode localNode = k8sNodeService.node(port.deviceId());
185 if (localNode == null) {
186 final String error = String.format("Cannot find kubernetes node for %s",
187 port.deviceId());
188 throw new IllegalStateException(error);
189 }
190 k8sNodeService.completeNodes().stream()
191 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
192 .forEach(remoteNode -> {
Jian Li619fa282020-09-02 14:45:35 +0900193 TrafficTreatment treatmentToTunnel = DefaultTrafficTreatment.builder()
194 .setOutput(remoteNode.intgToTunPortNum())
Jian Li4aa17642019-01-30 00:01:11 +0900195 .build();
196
197 k8sFlowRuleService.setRule(
198 appId,
199 remoteNode.intgBridge(),
200 selector,
Jian Li619fa282020-09-02 14:45:35 +0900201 treatmentToTunnel,
Jian Li4aa17642019-01-30 00:01:11 +0900202 PRIORITY_SWITCHING_RULE,
203 FORWARDING_TABLE,
204 install);
Jian Li619fa282020-09-02 14:45:35 +0900205
206 PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
207 k8sNetworkService, remoteNode);
208
209 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
210 .extension(buildExtension(
211 deviceService,
212 remoteNode.tunBridge(),
213 localNode.dataIp().getIp4Address()),
214 remoteNode.tunBridge())
215 .setTunnelId(getVni(port))
216 .setOutput(portNum)
217 .build();
218
219 k8sFlowRuleService.setRule(
220 appId,
221 remoteNode.tunBridge(),
222 selector,
223 treatmentToRemote,
224 PRIORITY_DEFAULT_RULE,
225 TUN_ENTRY_TABLE,
226 install);
Jian Li4aa17642019-01-30 00:01:11 +0900227 });
228 }
229
Jian Li619fa282020-09-02 14:45:35 +0900230 private void setRulesForTunnelBridge(K8sNode node, boolean install) {
231 setRulesForTunnelBridgeByType(node, K8sNetwork.Type.VXLAN, install);
232 setRulesForTunnelBridgeByType(node, K8sNetwork.Type.GRE, install);
233 setRulesForTunnelBridgeByType(node, K8sNetwork.Type.GENEVE, install);
234 }
235
236 private void setRulesForTunnelBridgeByType(K8sNode node, K8sNetwork.Type type, boolean install) {
237
238 PortNumber portNum;
239
240 switch (type) {
241 case VXLAN:
242 portNum = node.vxlanPortNum();
243 break;
244 case GRE:
245 portNum = node.grePortNum();
246 break;
247 case GENEVE:
248 portNum = node.genevePortNum();
249 break;
250 default:
251 return;
252 }
253
254 TrafficSelector inboundSelector = DefaultTrafficSelector.builder()
255 .matchInPort(portNum)
256 .build();
257
258 TrafficTreatment inboundTreatment = DefaultTrafficTreatment.builder()
259 .setOutput(node.tunToIntgPortNum())
260 .build();
261
262 k8sFlowRuleService.setRule(
263 appId,
264 node.tunBridge(),
265 inboundSelector,
266 inboundTreatment,
267 PRIORITY_DEFAULT_RULE,
268 TUN_ENTRY_TABLE,
269 install);
270 }
271
272
Jian Li4aa17642019-01-30 00:01:11 +0900273 private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
274 setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
275 }
276
277 private void setTunnelTagIpFlowRules(K8sPort port, boolean install) {
278 setTunnelTagFlowRules(port, Ethernet.TYPE_IPV4, install);
279 }
280
281 private void setNetworkRulesForTunnel(K8sPort port, boolean install) {
282 setTunnelTagIpFlowRules(port, install);
283 setForwardingRulesForTunnel(port, install);
284
285 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
286 setTunnelTagArpFlowRules(port, install);
287 }
288 }
289
290 /**
291 * Configures the flow rule which is for using VXLAN/GRE/GENEVE to tag the packet
292 * based on the in_port number of a virtual instance.
293 * Note that this rule will be inserted in vTag table.
294 *
295 * @param port kubernetes port object
296 * @param install install flag, add the rule if true, remove it otherwise
297 */
298 private void setTunnelTagFlowRules(K8sPort port, short ethType, boolean install) {
299 TrafficSelector selector = DefaultTrafficSelector.builder()
300 .matchEthType(ethType)
301 .matchInPort(port.portNumber())
302 .build();
303
304 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
305 .setTunnelId(getVni(port));
306
Jian Li4aa17642019-01-30 00:01:11 +0900307 if (ethType == Ethernet.TYPE_ARP) {
308 tBuilder.transition(ARP_TABLE);
309 } else if (ethType == Ethernet.TYPE_IPV4) {
Jian Li73d3b6a2019-07-08 18:07:53 +0900310 tBuilder.transition(JUMP_TABLE);
Jian Li4aa17642019-01-30 00:01:11 +0900311 }
312
313 k8sFlowRuleService.setRule(
314 appId,
315 port.deviceId(),
316 selector,
317 tBuilder.build(),
318 PRIORITY_TUNNEL_TAG_RULE,
319 VTAG_TABLE,
320 install);
321 }
322
Jian Lieb488ea2019-04-16 01:50:02 +0900323 private void setExtToIntgTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
324 TrafficSelector selector = DefaultTrafficSelector.builder()
325 .matchEthType(Ethernet.TYPE_IPV4)
Jian Lieb488ea2019-04-16 01:50:02 +0900326 .matchInPort(k8sNode.intgToExtPatchPortNum())
327 .build();
328
329 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
330
331 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
332 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900333 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900334
335 k8sFlowRuleService.setRule(
336 appId,
337 k8sNode.intgBridge(),
338 selector,
339 tBuilder.build(),
340 PRIORITY_TUNNEL_TAG_RULE,
341 VTAG_TABLE,
342 install);
343 }
344
345 private void setLocalTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
346 TrafficSelector selector = DefaultTrafficSelector.builder()
347 .matchEthType(Ethernet.TYPE_IPV4)
348 .matchInPort(PortNumber.LOCAL)
349 .build();
350
351 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
352
353 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
354 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900355 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900356
357 k8sFlowRuleService.setRule(
358 appId,
359 k8sNode.intgBridge(),
360 selector,
361 tBuilder.build(),
362 PRIORITY_TUNNEL_TAG_RULE,
363 VTAG_TABLE,
364 install);
365 }
366
Jian Li2cc2b632019-02-18 00:56:40 +0900367 private void setGatewayRulesForTunnel(boolean install) {
368 k8sNetworkService.networks().forEach(n -> {
369 // switching rules for the instPorts in the same node
370 TrafficSelector selector = DefaultTrafficSelector.builder()
371 // TODO: need to handle IPv6 in near future
372 .matchEthType(Ethernet.TYPE_IPV4)
373 .matchIPDst(IpPrefix.valueOf(n.gatewayIp(), 32))
374 .matchTunnelId(Long.valueOf(n.segmentId()))
375 .build();
376
377 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
378 .setOutput(PortNumber.LOCAL)
379 .build();
380
381 // FIXME: need to find a way to install the gateway rules into
382 // right OVS
383 k8sNodeService.completeNodes().forEach(node -> {
384 k8sFlowRuleService.setRule(
385 appId,
386 node.intgBridge(),
387 selector,
388 treatment,
389 PRIORITY_SWITCHING_RULE,
390 FORWARDING_TABLE,
391 install);
392 });
393 });
394 }
395
Jian Li4aa17642019-01-30 00:01:11 +0900396 /**
397 * Obtains the VNI from the given kubernetes port.
398 *
399 * @param port kubernetes port object
400 * @return Virtual Network Identifier (VNI)
401 */
402 private Long getVni(K8sPort port) {
403 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
404 if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
405 final String error =
406 String.format(ERR_SET_FLOWS_VNI,
407 port, k8sNet == null ? STR_NONE : k8sNet.name());
408 throw new IllegalStateException(error);
409 }
410 return Long.valueOf(k8sNet.segmentId());
411 }
412
413 private void setNetworkRules(K8sPort port, boolean install) {
414 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
415
416 if (k8sNet == null) {
Jian Li2cc2b632019-02-18 00:56:40 +0900417 log.warn("Network {} is not found from port {}.",
418 port.networkId(), port.portId());
Jian Li4aa17642019-01-30 00:01:11 +0900419 return;
420 }
421
422 switch (k8sNet.type()) {
423 case VXLAN:
424 case GRE:
425 case GENEVE:
426 setNetworkRulesForTunnel(port, install);
427 break;
428 default:
Jian Li2cc2b632019-02-18 00:56:40 +0900429 log.warn("The given network type {} is not supported.",
430 k8sNet.type().name());
Jian Li4aa17642019-01-30 00:01:11 +0900431 break;
432 }
433 }
434
435 private String getArpMode() {
436 Set<ConfigProperty> properties =
437 configService.getProperties(K8sSwitchingArpHandler.class.getName());
438 return getPropertyValue(properties, ARP_MODE);
439 }
440
441 private class InternalK8sNetworkListener implements K8sNetworkListener {
442
443 private boolean isRelevantHelper(K8sNetworkEvent event) {
444 return mastershipService.isLocalMaster(event.port().deviceId());
445 }
446
447 @Override
448 public void event(K8sNetworkEvent event) {
449 switch (event.type()) {
450 case K8S_PORT_ACTIVATED:
451 eventExecutor.execute(() -> processInstanceDetection(event));
452 break;
453 case K8S_PORT_REMOVED:
454 eventExecutor.execute(() -> processInstanceRemoval(event));
455 break;
456 default:
457 break;
458 }
459 }
460
461 private void processInstanceDetection(K8sNetworkEvent event) {
462 if (!isRelevantHelper(event)) {
463 return;
464 }
465
466 setNetworkRules(event.port(), true);
467 }
468
469 private void processInstanceRemoval(K8sNetworkEvent event) {
470 if (!isRelevantHelper(event)) {
471 return;
472 }
473
474 setNetworkRules(event.port(), false);
475 }
476 }
Jian Lieb488ea2019-04-16 01:50:02 +0900477
478 private class InternalK8sNodeListener implements K8sNodeListener {
479
480 private boolean isRelevantHelper() {
481 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
482 }
483
484 @Override
485 public void event(K8sNodeEvent event) {
486 switch (event.type()) {
487 case K8S_NODE_COMPLETE:
488 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
489 break;
490 default:
491 break;
492 }
493 }
494
495 private void processNodeCompletion(K8sNode k8sNode) {
496 if (!isRelevantHelper()) {
497 return;
498 }
499
500 setExtToIntgTunnelTagFlowRules(k8sNode, true);
501 setLocalTunnelTagFlowRules(k8sNode, true);
Jian Li619fa282020-09-02 14:45:35 +0900502 setRulesForTunnelBridge(k8sNode, true);
Jian Lieb488ea2019-04-16 01:50:02 +0900503 }
504 }
Jian Li4aa17642019-01-30 00:01:11 +0900505}