blob: 56c4398b83193a12882715362462368ca1b90205 [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;
64import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
65import static org.onosproject.k8snetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
66import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
67import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
68import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
69import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
70import static org.slf4j.LoggerFactory.getLogger;
71
72/**
73 * Populates switching flow rules on OVS for the basic connectivity among the
74 * container in the same network.
75 */
76@Component(immediate = true)
77public class K8sSwitchingHandler {
78
79 private final Logger log = getLogger(getClass());
80
81 private static final String ARP_MODE = "arpMode";
82 private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " +
83 "%s: Failed to get VNI for %s";
84 private static final String STR_NONE = "<none>";
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY)
87 protected CoreService coreService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 protected MastershipService mastershipService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieb488ea2019-04-16 01:50:02 +090093 protected ClusterService clusterService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +090096 protected DeviceService deviceService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 protected DriverService driverService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
102 protected ComponentConfigService configService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected LeadershipService leadershipService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected K8sFlowRuleService k8sFlowRuleService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected K8sNetworkService k8sNetworkService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected K8sNodeService k8sNodeService;
115
116 private final ExecutorService eventExecutor = newSingleThreadExecutor(
117 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
118 private final InternalK8sNetworkListener k8sNetworkListener =
119 new InternalK8sNetworkListener();
Jian Lieb488ea2019-04-16 01:50:02 +0900120 private final InternalK8sNodeListener k8sNodeListener =
121 new InternalK8sNodeListener();
Jian Li4aa17642019-01-30 00:01:11 +0900122
123 private ApplicationId appId;
Jian Lieb488ea2019-04-16 01:50:02 +0900124 private NodeId localNodeId;
Jian Li4aa17642019-01-30 00:01:11 +0900125
126 @Activate
127 protected void activate() {
128 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
129 k8sNetworkService.addListener(k8sNetworkListener);
Jian Lieb488ea2019-04-16 01:50:02 +0900130 localNodeId = clusterService.getLocalNode().id();
131 k8sNodeService.addListener(k8sNodeListener);
132 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900133
Jian Li2cc2b632019-02-18 00:56:40 +0900134 setGatewayRulesForTunnel(true);
135
Jian Li4aa17642019-01-30 00:01:11 +0900136 log.info("Started");
137 }
138
139 @Deactivate
140 protected void deactivate() {
Jian Lieb488ea2019-04-16 01:50:02 +0900141 leadershipService.withdraw(appId.name());
142 k8sNodeService.removeListener(k8sNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900143 k8sNetworkService.removeListener(k8sNetworkListener);
144 eventExecutor.shutdown();
145
Jian Li2cc2b632019-02-18 00:56:40 +0900146 setGatewayRulesForTunnel(false);
147
Jian Li4aa17642019-01-30 00:01:11 +0900148 log.info("Stopped");
149 }
150
151 /**
152 * Configures the flow rules which are used for L2 packet switching.
153 * Note that these rules will be inserted in switching table (table 5).
154 *
155 * @param port kubernetes port object
156 * @param install install flag, add the rule if true, remove it otherwise
157 */
158 private void setForwardingRulesForTunnel(K8sPort port, boolean install) {
159 // switching rules for the instPorts in the same node
160 TrafficSelector selector = DefaultTrafficSelector.builder()
161 // TODO: need to handle IPv6 in near future
162 .matchEthType(Ethernet.TYPE_IPV4)
163 .matchIPDst(port.ipAddress().toIpPrefix())
164 .matchTunnelId(getVni(port))
165 .build();
166
167 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
168 .setEthDst(port.macAddress())
169 .setOutput(port.portNumber())
170 .build();
171
172 k8sFlowRuleService.setRule(
173 appId,
174 port.deviceId(),
175 selector,
176 treatment,
177 PRIORITY_SWITCHING_RULE,
178 FORWARDING_TABLE,
179 install);
180
181 // switching rules for the node in the remote node
182 K8sNode localNode = k8sNodeService.node(port.deviceId());
183 if (localNode == null) {
184 final String error = String.format("Cannot find kubernetes node for %s",
185 port.deviceId());
186 throw new IllegalStateException(error);
187 }
188 k8sNodeService.completeNodes().stream()
189 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
190 .forEach(remoteNode -> {
191 PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
192 k8sNetworkService, remoteNode);
193 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
194 .extension(buildExtension(
195 deviceService,
196 remoteNode.intgBridge(),
197 localNode.dataIp().getIp4Address()),
198 remoteNode.intgBridge())
199 .setOutput(portNum)
200 .build();
201
202 k8sFlowRuleService.setRule(
203 appId,
204 remoteNode.intgBridge(),
205 selector,
206 treatmentToRemote,
207 PRIORITY_SWITCHING_RULE,
208 FORWARDING_TABLE,
209 install);
210 });
211 }
212
213 private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
214 setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
215 }
216
217 private void setTunnelTagIpFlowRules(K8sPort port, boolean install) {
218 setTunnelTagFlowRules(port, Ethernet.TYPE_IPV4, install);
219 }
220
221 private void setNetworkRulesForTunnel(K8sPort port, boolean install) {
222 setTunnelTagIpFlowRules(port, install);
223 setForwardingRulesForTunnel(port, install);
224
225 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
226 setTunnelTagArpFlowRules(port, install);
227 }
228 }
229
230 /**
231 * Configures the flow rule which is for using VXLAN/GRE/GENEVE to tag the packet
232 * based on the in_port number of a virtual instance.
233 * Note that this rule will be inserted in vTag table.
234 *
235 * @param port kubernetes port object
236 * @param install install flag, add the rule if true, remove it otherwise
237 */
238 private void setTunnelTagFlowRules(K8sPort port, short ethType, boolean install) {
239 TrafficSelector selector = DefaultTrafficSelector.builder()
240 .matchEthType(ethType)
241 .matchInPort(port.portNumber())
242 .build();
243
244 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
245 .setTunnelId(getVni(port));
246
Jian Li4aa17642019-01-30 00:01:11 +0900247 if (ethType == Ethernet.TYPE_ARP) {
248 tBuilder.transition(ARP_TABLE);
249 } else if (ethType == Ethernet.TYPE_IPV4) {
Jian Li73d3b6a2019-07-08 18:07:53 +0900250 tBuilder.transition(JUMP_TABLE);
Jian Li4aa17642019-01-30 00:01:11 +0900251 }
252
253 k8sFlowRuleService.setRule(
254 appId,
255 port.deviceId(),
256 selector,
257 tBuilder.build(),
258 PRIORITY_TUNNEL_TAG_RULE,
259 VTAG_TABLE,
260 install);
261 }
262
Jian Lieb488ea2019-04-16 01:50:02 +0900263 private void setExtToIntgTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
264 TrafficSelector selector = DefaultTrafficSelector.builder()
265 .matchEthType(Ethernet.TYPE_IPV4)
Jian Lieb488ea2019-04-16 01:50:02 +0900266 .matchInPort(k8sNode.intgToExtPatchPortNum())
267 .build();
268
269 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
270
271 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
272 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900273 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900274
275 k8sFlowRuleService.setRule(
276 appId,
277 k8sNode.intgBridge(),
278 selector,
279 tBuilder.build(),
280 PRIORITY_TUNNEL_TAG_RULE,
281 VTAG_TABLE,
282 install);
283 }
284
285 private void setLocalTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
286 TrafficSelector selector = DefaultTrafficSelector.builder()
287 .matchEthType(Ethernet.TYPE_IPV4)
288 .matchInPort(PortNumber.LOCAL)
289 .build();
290
291 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
292
293 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
294 .setTunnelId(Long.valueOf(net.segmentId()))
Jian Li73d3b6a2019-07-08 18:07:53 +0900295 .transition(JUMP_TABLE);
Jian Lieb488ea2019-04-16 01:50:02 +0900296
297 k8sFlowRuleService.setRule(
298 appId,
299 k8sNode.intgBridge(),
300 selector,
301 tBuilder.build(),
302 PRIORITY_TUNNEL_TAG_RULE,
303 VTAG_TABLE,
304 install);
305 }
306
Jian Li2cc2b632019-02-18 00:56:40 +0900307 private void setGatewayRulesForTunnel(boolean install) {
308 k8sNetworkService.networks().forEach(n -> {
309 // switching rules for the instPorts in the same node
310 TrafficSelector selector = DefaultTrafficSelector.builder()
311 // TODO: need to handle IPv6 in near future
312 .matchEthType(Ethernet.TYPE_IPV4)
313 .matchIPDst(IpPrefix.valueOf(n.gatewayIp(), 32))
314 .matchTunnelId(Long.valueOf(n.segmentId()))
315 .build();
316
317 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
318 .setOutput(PortNumber.LOCAL)
319 .build();
320
321 // FIXME: need to find a way to install the gateway rules into
322 // right OVS
323 k8sNodeService.completeNodes().forEach(node -> {
324 k8sFlowRuleService.setRule(
325 appId,
326 node.intgBridge(),
327 selector,
328 treatment,
329 PRIORITY_SWITCHING_RULE,
330 FORWARDING_TABLE,
331 install);
332 });
333 });
334 }
335
Jian Li4aa17642019-01-30 00:01:11 +0900336 /**
337 * Obtains the VNI from the given kubernetes port.
338 *
339 * @param port kubernetes port object
340 * @return Virtual Network Identifier (VNI)
341 */
342 private Long getVni(K8sPort port) {
343 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
344 if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
345 final String error =
346 String.format(ERR_SET_FLOWS_VNI,
347 port, k8sNet == null ? STR_NONE : k8sNet.name());
348 throw new IllegalStateException(error);
349 }
350 return Long.valueOf(k8sNet.segmentId());
351 }
352
353 private void setNetworkRules(K8sPort port, boolean install) {
354 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
355
356 if (k8sNet == null) {
Jian Li2cc2b632019-02-18 00:56:40 +0900357 log.warn("Network {} is not found from port {}.",
358 port.networkId(), port.portId());
Jian Li4aa17642019-01-30 00:01:11 +0900359 return;
360 }
361
362 switch (k8sNet.type()) {
363 case VXLAN:
364 case GRE:
365 case GENEVE:
366 setNetworkRulesForTunnel(port, install);
367 break;
368 default:
Jian Li2cc2b632019-02-18 00:56:40 +0900369 log.warn("The given network type {} is not supported.",
370 k8sNet.type().name());
Jian Li4aa17642019-01-30 00:01:11 +0900371 break;
372 }
373 }
374
375 private String getArpMode() {
376 Set<ConfigProperty> properties =
377 configService.getProperties(K8sSwitchingArpHandler.class.getName());
378 return getPropertyValue(properties, ARP_MODE);
379 }
380
381 private class InternalK8sNetworkListener implements K8sNetworkListener {
382
383 private boolean isRelevantHelper(K8sNetworkEvent event) {
384 return mastershipService.isLocalMaster(event.port().deviceId());
385 }
386
387 @Override
388 public void event(K8sNetworkEvent event) {
389 switch (event.type()) {
390 case K8S_PORT_ACTIVATED:
391 eventExecutor.execute(() -> processInstanceDetection(event));
392 break;
393 case K8S_PORT_REMOVED:
394 eventExecutor.execute(() -> processInstanceRemoval(event));
395 break;
396 default:
397 break;
398 }
399 }
400
401 private void processInstanceDetection(K8sNetworkEvent event) {
402 if (!isRelevantHelper(event)) {
403 return;
404 }
405
406 setNetworkRules(event.port(), true);
407 }
408
409 private void processInstanceRemoval(K8sNetworkEvent event) {
410 if (!isRelevantHelper(event)) {
411 return;
412 }
413
414 setNetworkRules(event.port(), false);
415 }
416 }
Jian Lieb488ea2019-04-16 01:50:02 +0900417
418 private class InternalK8sNodeListener implements K8sNodeListener {
419
420 private boolean isRelevantHelper() {
421 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
422 }
423
424 @Override
425 public void event(K8sNodeEvent event) {
426 switch (event.type()) {
427 case K8S_NODE_COMPLETE:
428 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
429 break;
430 default:
431 break;
432 }
433 }
434
435 private void processNodeCompletion(K8sNode k8sNode) {
436 if (!isRelevantHelper()) {
437 return;
438 }
439
440 setExtToIntgTunnelTagFlowRules(k8sNode, true);
441 setLocalTunnelTagFlowRules(k8sNode, true);
442 }
443 }
Jian Li4aa17642019-01-30 00:01:11 +0900444}