blob: d62a923a1e43067d1e68938493515ab907cc33ec [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;
Jian Li732c3422020-09-07 17:01:11 +090069import static org.onosproject.k8snetworking.api.K8sNetwork.Type.GENEVE;
70import static org.onosproject.k8snetworking.api.K8sNetwork.Type.GRE;
71import static org.onosproject.k8snetworking.api.K8sNetwork.Type.VXLAN;
Jian Li4aa17642019-01-30 00:01:11 +090072import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
73import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
Jian Li732c3422020-09-07 17:01:11 +090074import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetType;
Jian Li4aa17642019-01-30 00:01:11 +090075import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
76import static org.slf4j.LoggerFactory.getLogger;
77
78/**
79 * Populates switching flow rules on OVS for the basic connectivity among the
80 * container in the same network.
81 */
82@Component(immediate = true)
83public class K8sSwitchingHandler {
84
85 private final Logger log = getLogger(getClass());
86
87 private static final String ARP_MODE = "arpMode";
88 private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " +
89 "%s: Failed to get VNI for %s";
90 private static final String STR_NONE = "<none>";
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 protected CoreService coreService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected MastershipService mastershipService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieb488ea2019-04-16 01:50:02 +090099 protected ClusterService clusterService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +0900102 protected DeviceService deviceService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected DriverService driverService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected ComponentConfigService configService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected LeadershipService leadershipService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected K8sFlowRuleService k8sFlowRuleService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected K8sNetworkService k8sNetworkService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected K8sNodeService k8sNodeService;
121
122 private final ExecutorService eventExecutor = newSingleThreadExecutor(
123 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
124 private final InternalK8sNetworkListener k8sNetworkListener =
125 new InternalK8sNetworkListener();
Jian Lieb488ea2019-04-16 01:50:02 +0900126 private final InternalK8sNodeListener k8sNodeListener =
127 new InternalK8sNodeListener();
Jian Li4aa17642019-01-30 00:01:11 +0900128
129 private ApplicationId appId;
Jian Lieb488ea2019-04-16 01:50:02 +0900130 private NodeId localNodeId;
Jian Li4aa17642019-01-30 00:01:11 +0900131
132 @Activate
133 protected void activate() {
134 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
135 k8sNetworkService.addListener(k8sNetworkListener);
Jian Lieb488ea2019-04-16 01:50:02 +0900136 localNodeId = clusterService.getLocalNode().id();
137 k8sNodeService.addListener(k8sNodeListener);
138 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900139
Jian Li2cc2b632019-02-18 00:56:40 +0900140 setGatewayRulesForTunnel(true);
141
Jian Li4aa17642019-01-30 00:01:11 +0900142 log.info("Started");
143 }
144
145 @Deactivate
146 protected void deactivate() {
Jian Lieb488ea2019-04-16 01:50:02 +0900147 leadershipService.withdraw(appId.name());
148 k8sNodeService.removeListener(k8sNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900149 k8sNetworkService.removeListener(k8sNetworkListener);
150 eventExecutor.shutdown();
151
Jian Li2cc2b632019-02-18 00:56:40 +0900152 setGatewayRulesForTunnel(false);
153
Jian Li4aa17642019-01-30 00:01:11 +0900154 log.info("Stopped");
155 }
156
157 /**
158 * Configures the flow rules which are used for L2 packet switching.
Jian Li619fa282020-09-02 14:45:35 +0900159 * Note that these rules will be inserted in switching table (table 80).
Jian Li4aa17642019-01-30 00:01:11 +0900160 *
161 * @param port kubernetes port object
162 * @param install install flag, add the rule if true, remove it otherwise
163 */
164 private void setForwardingRulesForTunnel(K8sPort port, boolean install) {
165 // switching rules for the instPorts in the same node
166 TrafficSelector selector = DefaultTrafficSelector.builder()
167 // TODO: need to handle IPv6 in near future
168 .matchEthType(Ethernet.TYPE_IPV4)
169 .matchIPDst(port.ipAddress().toIpPrefix())
Jian Li619fa282020-09-02 14:45:35 +0900170 // .matchTunnelId(getVni(port))
Jian Li4aa17642019-01-30 00:01:11 +0900171 .build();
172
173 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
174 .setEthDst(port.macAddress())
175 .setOutput(port.portNumber())
176 .build();
177
178 k8sFlowRuleService.setRule(
179 appId,
180 port.deviceId(),
181 selector,
182 treatment,
183 PRIORITY_SWITCHING_RULE,
184 FORWARDING_TABLE,
185 install);
186
187 // switching rules for the node in the remote node
188 K8sNode localNode = k8sNodeService.node(port.deviceId());
189 if (localNode == null) {
190 final String error = String.format("Cannot find kubernetes node for %s",
191 port.deviceId());
192 throw new IllegalStateException(error);
193 }
194 k8sNodeService.completeNodes().stream()
195 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
196 .forEach(remoteNode -> {
Jian Li619fa282020-09-02 14:45:35 +0900197 TrafficTreatment treatmentToTunnel = DefaultTrafficTreatment.builder()
198 .setOutput(remoteNode.intgToTunPortNum())
Jian Li4aa17642019-01-30 00:01:11 +0900199 .build();
200
201 k8sFlowRuleService.setRule(
202 appId,
203 remoteNode.intgBridge(),
204 selector,
Jian Li619fa282020-09-02 14:45:35 +0900205 treatmentToTunnel,
Jian Li4aa17642019-01-30 00:01:11 +0900206 PRIORITY_SWITCHING_RULE,
207 FORWARDING_TABLE,
208 install);
Jian Li619fa282020-09-02 14:45:35 +0900209
210 PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
211 k8sNetworkService, remoteNode);
212
213 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
214 .extension(buildExtension(
215 deviceService,
216 remoteNode.tunBridge(),
217 localNode.dataIp().getIp4Address()),
218 remoteNode.tunBridge())
219 .setTunnelId(getVni(port))
220 .setOutput(portNum)
221 .build();
222
223 k8sFlowRuleService.setRule(
224 appId,
225 remoteNode.tunBridge(),
226 selector,
227 treatmentToRemote,
228 PRIORITY_DEFAULT_RULE,
229 TUN_ENTRY_TABLE,
230 install);
Jian Li4aa17642019-01-30 00:01:11 +0900231 });
232 }
233
Jian Li619fa282020-09-02 14:45:35 +0900234 private void setRulesForTunnelBridge(K8sNode node, boolean install) {
Jian Li732c3422020-09-07 17:01:11 +0900235 setRulesForTunnelBridgeByType(node, VXLAN, install);
236 setRulesForTunnelBridgeByType(node, GRE, install);
237 setRulesForTunnelBridgeByType(node, GENEVE, install);
Jian Li619fa282020-09-02 14:45:35 +0900238 }
239
240 private void setRulesForTunnelBridgeByType(K8sNode node, K8sNetwork.Type type, boolean install) {
241
242 PortNumber portNum;
243
244 switch (type) {
245 case VXLAN:
Jian Li732c3422020-09-07 17:01:11 +0900246 portNum = tunnelPortNumByNetType(VXLAN, node);
Jian Li619fa282020-09-02 14:45:35 +0900247 break;
248 case GRE:
Jian Li732c3422020-09-07 17:01:11 +0900249 portNum = tunnelPortNumByNetType(GRE, node);
Jian Li619fa282020-09-02 14:45:35 +0900250 break;
251 case GENEVE:
Jian Li732c3422020-09-07 17:01:11 +0900252 portNum = tunnelPortNumByNetType(GENEVE, node);
Jian Li619fa282020-09-02 14:45:35 +0900253 break;
254 default:
255 return;
256 }
257
258 TrafficSelector inboundSelector = DefaultTrafficSelector.builder()
259 .matchInPort(portNum)
260 .build();
261
262 TrafficTreatment inboundTreatment = DefaultTrafficTreatment.builder()
263 .setOutput(node.tunToIntgPortNum())
264 .build();
265
266 k8sFlowRuleService.setRule(
267 appId,
268 node.tunBridge(),
269 inboundSelector,
270 inboundTreatment,
271 PRIORITY_DEFAULT_RULE,
272 TUN_ENTRY_TABLE,
273 install);
274 }
275
276
Jian Li4aa17642019-01-30 00:01:11 +0900277 private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
278 setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
279 }
280
281 private void setTunnelTagIpFlowRules(K8sPort port, boolean install) {
282 setTunnelTagFlowRules(port, Ethernet.TYPE_IPV4, install);
283 }
284
285 private void setNetworkRulesForTunnel(K8sPort port, boolean install) {
286 setTunnelTagIpFlowRules(port, install);
287 setForwardingRulesForTunnel(port, install);
288
289 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
290 setTunnelTagArpFlowRules(port, install);
291 }
292 }
293
294 /**
295 * Configures the flow rule which is for using VXLAN/GRE/GENEVE to tag the packet
296 * based on the in_port number of a virtual instance.
297 * Note that this rule will be inserted in vTag table.
298 *
299 * @param port kubernetes port object
300 * @param install install flag, add the rule if true, remove it otherwise
301 */
302 private void setTunnelTagFlowRules(K8sPort port, short ethType, boolean install) {
303 TrafficSelector selector = DefaultTrafficSelector.builder()
304 .matchEthType(ethType)
305 .matchInPort(port.portNumber())
306 .build();
307
308 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
309 .setTunnelId(getVni(port));
310
Jian Li4aa17642019-01-30 00:01:11 +0900311 if (ethType == Ethernet.TYPE_ARP) {
312 tBuilder.transition(ARP_TABLE);
313 } else if (ethType == Ethernet.TYPE_IPV4) {
Jian Li73d3b6a2019-07-08 18:07:53 +0900314 tBuilder.transition(JUMP_TABLE);
Jian Li4aa17642019-01-30 00:01:11 +0900315 }
316
317 k8sFlowRuleService.setRule(
318 appId,
319 port.deviceId(),
320 selector,
321 tBuilder.build(),
322 PRIORITY_TUNNEL_TAG_RULE,
323 VTAG_TABLE,
324 install);
325 }
326
Jian Lieb488ea2019-04-16 01:50:02 +0900327 private void setExtToIntgTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
328 TrafficSelector selector = DefaultTrafficSelector.builder()
329 .matchEthType(Ethernet.TYPE_IPV4)
Jian Lieb488ea2019-04-16 01:50:02 +0900330 .matchInPort(k8sNode.intgToExtPatchPortNum())
331 .build();
332
333 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
334
335 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
336 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900337 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900338
339 k8sFlowRuleService.setRule(
340 appId,
341 k8sNode.intgBridge(),
342 selector,
343 tBuilder.build(),
344 PRIORITY_TUNNEL_TAG_RULE,
345 VTAG_TABLE,
346 install);
347 }
348
349 private void setLocalTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
350 TrafficSelector selector = DefaultTrafficSelector.builder()
351 .matchEthType(Ethernet.TYPE_IPV4)
352 .matchInPort(PortNumber.LOCAL)
353 .build();
354
355 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
356
357 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
358 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900359 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900360
361 k8sFlowRuleService.setRule(
362 appId,
363 k8sNode.intgBridge(),
364 selector,
365 tBuilder.build(),
366 PRIORITY_TUNNEL_TAG_RULE,
367 VTAG_TABLE,
368 install);
369 }
370
Jian Li2cc2b632019-02-18 00:56:40 +0900371 private void setGatewayRulesForTunnel(boolean install) {
372 k8sNetworkService.networks().forEach(n -> {
373 // switching rules for the instPorts in the same node
374 TrafficSelector selector = DefaultTrafficSelector.builder()
375 // TODO: need to handle IPv6 in near future
376 .matchEthType(Ethernet.TYPE_IPV4)
377 .matchIPDst(IpPrefix.valueOf(n.gatewayIp(), 32))
378 .matchTunnelId(Long.valueOf(n.segmentId()))
379 .build();
380
381 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
382 .setOutput(PortNumber.LOCAL)
383 .build();
384
385 // FIXME: need to find a way to install the gateway rules into
386 // right OVS
387 k8sNodeService.completeNodes().forEach(node -> {
388 k8sFlowRuleService.setRule(
389 appId,
390 node.intgBridge(),
391 selector,
392 treatment,
393 PRIORITY_SWITCHING_RULE,
394 FORWARDING_TABLE,
395 install);
396 });
397 });
398 }
399
Jian Li4aa17642019-01-30 00:01:11 +0900400 /**
401 * Obtains the VNI from the given kubernetes port.
402 *
403 * @param port kubernetes port object
404 * @return Virtual Network Identifier (VNI)
405 */
406 private Long getVni(K8sPort port) {
407 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
408 if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
409 final String error =
410 String.format(ERR_SET_FLOWS_VNI,
411 port, k8sNet == null ? STR_NONE : k8sNet.name());
412 throw new IllegalStateException(error);
413 }
414 return Long.valueOf(k8sNet.segmentId());
415 }
416
417 private void setNetworkRules(K8sPort port, boolean install) {
418 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
419
420 if (k8sNet == null) {
Jian Li2cc2b632019-02-18 00:56:40 +0900421 log.warn("Network {} is not found from port {}.",
422 port.networkId(), port.portId());
Jian Li4aa17642019-01-30 00:01:11 +0900423 return;
424 }
425
426 switch (k8sNet.type()) {
427 case VXLAN:
428 case GRE:
429 case GENEVE:
430 setNetworkRulesForTunnel(port, install);
431 break;
432 default:
Jian Li2cc2b632019-02-18 00:56:40 +0900433 log.warn("The given network type {} is not supported.",
434 k8sNet.type().name());
Jian Li4aa17642019-01-30 00:01:11 +0900435 break;
436 }
437 }
438
439 private String getArpMode() {
440 Set<ConfigProperty> properties =
441 configService.getProperties(K8sSwitchingArpHandler.class.getName());
442 return getPropertyValue(properties, ARP_MODE);
443 }
444
445 private class InternalK8sNetworkListener implements K8sNetworkListener {
446
447 private boolean isRelevantHelper(K8sNetworkEvent event) {
448 return mastershipService.isLocalMaster(event.port().deviceId());
449 }
450
451 @Override
452 public void event(K8sNetworkEvent event) {
453 switch (event.type()) {
454 case K8S_PORT_ACTIVATED:
455 eventExecutor.execute(() -> processInstanceDetection(event));
456 break;
457 case K8S_PORT_REMOVED:
458 eventExecutor.execute(() -> processInstanceRemoval(event));
459 break;
460 default:
461 break;
462 }
463 }
464
465 private void processInstanceDetection(K8sNetworkEvent event) {
466 if (!isRelevantHelper(event)) {
467 return;
468 }
469
470 setNetworkRules(event.port(), true);
471 }
472
473 private void processInstanceRemoval(K8sNetworkEvent event) {
474 if (!isRelevantHelper(event)) {
475 return;
476 }
477
478 setNetworkRules(event.port(), false);
479 }
480 }
Jian Lieb488ea2019-04-16 01:50:02 +0900481
482 private class InternalK8sNodeListener implements K8sNodeListener {
483
484 private boolean isRelevantHelper() {
485 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
486 }
487
488 @Override
489 public void event(K8sNodeEvent event) {
490 switch (event.type()) {
491 case K8S_NODE_COMPLETE:
492 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
493 break;
494 default:
495 break;
496 }
497 }
498
499 private void processNodeCompletion(K8sNode k8sNode) {
500 if (!isRelevantHelper()) {
501 return;
502 }
503
504 setExtToIntgTunnelTagFlowRules(k8sNode, true);
505 setLocalTunnelTagFlowRules(k8sNode, true);
Jian Li619fa282020-09-02 14:45:35 +0900506 setRulesForTunnelBridge(k8sNode, true);
Jian Lieb488ea2019-04-16 01:50:02 +0900507 }
508 }
Jian Li4aa17642019-01-30 00:01:11 +0900509}