blob: 409d80b4bbf9d48b6a22235923ba6834aa0092f0 [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;
59import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
60import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
61import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
62import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
63import 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
247
248 if (ethType == Ethernet.TYPE_ARP) {
249 tBuilder.transition(ARP_TABLE);
250 } else if (ethType == Ethernet.TYPE_IPV4) {
251 tBuilder.transition(ACL_EGRESS_TABLE);
252 }
253
254 k8sFlowRuleService.setRule(
255 appId,
256 port.deviceId(),
257 selector,
258 tBuilder.build(),
259 PRIORITY_TUNNEL_TAG_RULE,
260 VTAG_TABLE,
261 install);
262 }
263
Jian Lieb488ea2019-04-16 01:50:02 +0900264 private void setExtToIntgTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
265 TrafficSelector selector = DefaultTrafficSelector.builder()
266 .matchEthType(Ethernet.TYPE_IPV4)
Jian Lieb488ea2019-04-16 01:50:02 +0900267 .matchInPort(k8sNode.intgToExtPatchPortNum())
268 .build();
269
270 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
271
272 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
273 .setTunnelId(Long.valueOf(net.segmentId()))
274 .transition(ACL_EGRESS_TABLE);
275
276 k8sFlowRuleService.setRule(
277 appId,
278 k8sNode.intgBridge(),
279 selector,
280 tBuilder.build(),
281 PRIORITY_TUNNEL_TAG_RULE,
282 VTAG_TABLE,
283 install);
284 }
285
286 private void setLocalTunnelTagFlowRules(K8sNode k8sNode, boolean install) {
287 TrafficSelector selector = DefaultTrafficSelector.builder()
288 .matchEthType(Ethernet.TYPE_IPV4)
289 .matchInPort(PortNumber.LOCAL)
290 .build();
291
292 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
293
294 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
295 .setTunnelId(Long.valueOf(net.segmentId()))
296 .transition(ACL_EGRESS_TABLE);
297
298 k8sFlowRuleService.setRule(
299 appId,
300 k8sNode.intgBridge(),
301 selector,
302 tBuilder.build(),
303 PRIORITY_TUNNEL_TAG_RULE,
304 VTAG_TABLE,
305 install);
306 }
307
Jian Li2cc2b632019-02-18 00:56:40 +0900308 private void setGatewayRulesForTunnel(boolean install) {
309 k8sNetworkService.networks().forEach(n -> {
310 // switching rules for the instPorts in the same node
311 TrafficSelector selector = DefaultTrafficSelector.builder()
312 // TODO: need to handle IPv6 in near future
313 .matchEthType(Ethernet.TYPE_IPV4)
314 .matchIPDst(IpPrefix.valueOf(n.gatewayIp(), 32))
315 .matchTunnelId(Long.valueOf(n.segmentId()))
316 .build();
317
318 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
319 .setOutput(PortNumber.LOCAL)
320 .build();
321
322 // FIXME: need to find a way to install the gateway rules into
323 // right OVS
324 k8sNodeService.completeNodes().forEach(node -> {
325 k8sFlowRuleService.setRule(
326 appId,
327 node.intgBridge(),
328 selector,
329 treatment,
330 PRIORITY_SWITCHING_RULE,
331 FORWARDING_TABLE,
332 install);
333 });
334 });
335 }
336
Jian Li4aa17642019-01-30 00:01:11 +0900337 /**
338 * Obtains the VNI from the given kubernetes port.
339 *
340 * @param port kubernetes port object
341 * @return Virtual Network Identifier (VNI)
342 */
343 private Long getVni(K8sPort port) {
344 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
345 if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
346 final String error =
347 String.format(ERR_SET_FLOWS_VNI,
348 port, k8sNet == null ? STR_NONE : k8sNet.name());
349 throw new IllegalStateException(error);
350 }
351 return Long.valueOf(k8sNet.segmentId());
352 }
353
354 private void setNetworkRules(K8sPort port, boolean install) {
355 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
356
357 if (k8sNet == null) {
Jian Li2cc2b632019-02-18 00:56:40 +0900358 log.warn("Network {} is not found from port {}.",
359 port.networkId(), port.portId());
Jian Li4aa17642019-01-30 00:01:11 +0900360 return;
361 }
362
363 switch (k8sNet.type()) {
364 case VXLAN:
365 case GRE:
366 case GENEVE:
367 setNetworkRulesForTunnel(port, install);
368 break;
369 default:
Jian Li2cc2b632019-02-18 00:56:40 +0900370 log.warn("The given network type {} is not supported.",
371 k8sNet.type().name());
Jian Li4aa17642019-01-30 00:01:11 +0900372 break;
373 }
374 }
375
376 private String getArpMode() {
377 Set<ConfigProperty> properties =
378 configService.getProperties(K8sSwitchingArpHandler.class.getName());
379 return getPropertyValue(properties, ARP_MODE);
380 }
381
382 private class InternalK8sNetworkListener implements K8sNetworkListener {
383
384 private boolean isRelevantHelper(K8sNetworkEvent event) {
385 return mastershipService.isLocalMaster(event.port().deviceId());
386 }
387
388 @Override
389 public void event(K8sNetworkEvent event) {
390 switch (event.type()) {
391 case K8S_PORT_ACTIVATED:
392 eventExecutor.execute(() -> processInstanceDetection(event));
393 break;
394 case K8S_PORT_REMOVED:
395 eventExecutor.execute(() -> processInstanceRemoval(event));
396 break;
397 default:
398 break;
399 }
400 }
401
402 private void processInstanceDetection(K8sNetworkEvent event) {
403 if (!isRelevantHelper(event)) {
404 return;
405 }
406
407 setNetworkRules(event.port(), true);
408 }
409
410 private void processInstanceRemoval(K8sNetworkEvent event) {
411 if (!isRelevantHelper(event)) {
412 return;
413 }
414
415 setNetworkRules(event.port(), false);
416 }
417 }
Jian Lieb488ea2019-04-16 01:50:02 +0900418
419 private class InternalK8sNodeListener implements K8sNodeListener {
420
421 private boolean isRelevantHelper() {
422 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
423 }
424
425 @Override
426 public void event(K8sNodeEvent event) {
427 switch (event.type()) {
428 case K8S_NODE_COMPLETE:
429 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
430 break;
431 default:
432 break;
433 }
434 }
435
436 private void processNodeCompletion(K8sNode k8sNode) {
437 if (!isRelevantHelper()) {
438 return;
439 }
440
441 setExtToIntgTunnelTagFlowRules(k8sNode, true);
442 setLocalTunnelTagFlowRules(k8sNode, true);
443 }
444 }
Jian Li4aa17642019-01-30 00:01:11 +0900445}