blob: 88cb5893b7cb513c953bbabe828c91bc763a8ac4 [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;
23import org.onosproject.cluster.LeadershipService;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.k8snetworking.api.K8sFlowRuleService;
27import org.onosproject.k8snetworking.api.K8sNetwork;
28import org.onosproject.k8snetworking.api.K8sNetworkEvent;
29import org.onosproject.k8snetworking.api.K8sNetworkListener;
30import org.onosproject.k8snetworking.api.K8sNetworkService;
31import org.onosproject.k8snetworking.api.K8sPort;
32import org.onosproject.k8snode.api.K8sNode;
33import org.onosproject.k8snode.api.K8sNodeService;
34import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.driver.DriverService;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
42import org.osgi.service.component.annotations.Activate;
43import org.osgi.service.component.annotations.Component;
44import org.osgi.service.component.annotations.Deactivate;
45import org.osgi.service.component.annotations.Reference;
46import org.osgi.service.component.annotations.ReferenceCardinality;
47import org.slf4j.Logger;
48
49import java.util.Set;
50import java.util.concurrent.ExecutorService;
51
52import static java.util.concurrent.Executors.newSingleThreadExecutor;
53import static org.onlab.util.Tools.groupedThreads;
54import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
55import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
56import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
57import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
58import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
59import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
60import static org.onosproject.k8snetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
61import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
62import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
63import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
64import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
65import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Populates switching flow rules on OVS for the basic connectivity among the
69 * container in the same network.
70 */
71@Component(immediate = true)
72public class K8sSwitchingHandler {
73
74 private final Logger log = getLogger(getClass());
75
76 private static final String ARP_MODE = "arpMode";
77 private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for " +
78 "%s: Failed to get VNI for %s";
79 private static final String STR_NONE = "<none>";
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY)
82 protected CoreService coreService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY)
85 protected MastershipService mastershipService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY)
88 protected DeviceService deviceService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY)
91 protected DriverService driverService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected ComponentConfigService configService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected LeadershipService leadershipService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected K8sFlowRuleService k8sFlowRuleService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected K8sNetworkService k8sNetworkService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected K8sNodeService k8sNodeService;
107
108 private final ExecutorService eventExecutor = newSingleThreadExecutor(
109 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
110 private final InternalK8sNetworkListener k8sNetworkListener =
111 new InternalK8sNetworkListener();
112
113 private ApplicationId appId;
114
115 @Activate
116 protected void activate() {
117 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
118 k8sNetworkService.addListener(k8sNetworkListener);
119
Jian Li2cc2b632019-02-18 00:56:40 +0900120 setGatewayRulesForTunnel(true);
121
Jian Li4aa17642019-01-30 00:01:11 +0900122 log.info("Started");
123 }
124
125 @Deactivate
126 protected void deactivate() {
127 k8sNetworkService.removeListener(k8sNetworkListener);
128 eventExecutor.shutdown();
129
Jian Li2cc2b632019-02-18 00:56:40 +0900130 setGatewayRulesForTunnel(false);
131
Jian Li4aa17642019-01-30 00:01:11 +0900132 log.info("Stopped");
133 }
134
135 /**
136 * Configures the flow rules which are used for L2 packet switching.
137 * Note that these rules will be inserted in switching table (table 5).
138 *
139 * @param port kubernetes port object
140 * @param install install flag, add the rule if true, remove it otherwise
141 */
142 private void setForwardingRulesForTunnel(K8sPort port, boolean install) {
143 // switching rules for the instPorts in the same node
144 TrafficSelector selector = DefaultTrafficSelector.builder()
145 // TODO: need to handle IPv6 in near future
146 .matchEthType(Ethernet.TYPE_IPV4)
147 .matchIPDst(port.ipAddress().toIpPrefix())
148 .matchTunnelId(getVni(port))
149 .build();
150
151 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
152 .setEthDst(port.macAddress())
153 .setOutput(port.portNumber())
154 .build();
155
156 k8sFlowRuleService.setRule(
157 appId,
158 port.deviceId(),
159 selector,
160 treatment,
161 PRIORITY_SWITCHING_RULE,
162 FORWARDING_TABLE,
163 install);
164
165 // switching rules for the node in the remote node
166 K8sNode localNode = k8sNodeService.node(port.deviceId());
167 if (localNode == null) {
168 final String error = String.format("Cannot find kubernetes node for %s",
169 port.deviceId());
170 throw new IllegalStateException(error);
171 }
172 k8sNodeService.completeNodes().stream()
173 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
174 .forEach(remoteNode -> {
175 PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
176 k8sNetworkService, remoteNode);
177 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
178 .extension(buildExtension(
179 deviceService,
180 remoteNode.intgBridge(),
181 localNode.dataIp().getIp4Address()),
182 remoteNode.intgBridge())
183 .setOutput(portNum)
184 .build();
185
186 k8sFlowRuleService.setRule(
187 appId,
188 remoteNode.intgBridge(),
189 selector,
190 treatmentToRemote,
191 PRIORITY_SWITCHING_RULE,
192 FORWARDING_TABLE,
193 install);
194 });
195 }
196
197 private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
198 setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
199 }
200
201 private void setTunnelTagIpFlowRules(K8sPort port, boolean install) {
202 setTunnelTagFlowRules(port, Ethernet.TYPE_IPV4, install);
203 }
204
205 private void setNetworkRulesForTunnel(K8sPort port, boolean install) {
206 setTunnelTagIpFlowRules(port, install);
207 setForwardingRulesForTunnel(port, install);
208
209 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
210 setTunnelTagArpFlowRules(port, install);
211 }
212 }
213
214 /**
215 * Configures the flow rule which is for using VXLAN/GRE/GENEVE to tag the packet
216 * based on the in_port number of a virtual instance.
217 * Note that this rule will be inserted in vTag table.
218 *
219 * @param port kubernetes port object
220 * @param install install flag, add the rule if true, remove it otherwise
221 */
222 private void setTunnelTagFlowRules(K8sPort port, short ethType, boolean install) {
223 TrafficSelector selector = DefaultTrafficSelector.builder()
224 .matchEthType(ethType)
225 .matchInPort(port.portNumber())
226 .build();
227
228 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
229 .setTunnelId(getVni(port));
230
231
232 if (ethType == Ethernet.TYPE_ARP) {
233 tBuilder.transition(ARP_TABLE);
234 } else if (ethType == Ethernet.TYPE_IPV4) {
235 tBuilder.transition(ACL_EGRESS_TABLE);
236 }
237
238 k8sFlowRuleService.setRule(
239 appId,
240 port.deviceId(),
241 selector,
242 tBuilder.build(),
243 PRIORITY_TUNNEL_TAG_RULE,
244 VTAG_TABLE,
245 install);
246 }
247
Jian Li2cc2b632019-02-18 00:56:40 +0900248 private void setGatewayRulesForTunnel(boolean install) {
249 k8sNetworkService.networks().forEach(n -> {
250 // switching rules for the instPorts in the same node
251 TrafficSelector selector = DefaultTrafficSelector.builder()
252 // TODO: need to handle IPv6 in near future
253 .matchEthType(Ethernet.TYPE_IPV4)
254 .matchIPDst(IpPrefix.valueOf(n.gatewayIp(), 32))
255 .matchTunnelId(Long.valueOf(n.segmentId()))
256 .build();
257
258 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
259 .setOutput(PortNumber.LOCAL)
260 .build();
261
262 // FIXME: need to find a way to install the gateway rules into
263 // right OVS
264 k8sNodeService.completeNodes().forEach(node -> {
265 k8sFlowRuleService.setRule(
266 appId,
267 node.intgBridge(),
268 selector,
269 treatment,
270 PRIORITY_SWITCHING_RULE,
271 FORWARDING_TABLE,
272 install);
273 });
274 });
275 }
276
Jian Li4aa17642019-01-30 00:01:11 +0900277 /**
278 * Obtains the VNI from the given kubernetes port.
279 *
280 * @param port kubernetes port object
281 * @return Virtual Network Identifier (VNI)
282 */
283 private Long getVni(K8sPort port) {
284 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
285 if (k8sNet == null || Strings.isNullOrEmpty(k8sNet.segmentId())) {
286 final String error =
287 String.format(ERR_SET_FLOWS_VNI,
288 port, k8sNet == null ? STR_NONE : k8sNet.name());
289 throw new IllegalStateException(error);
290 }
291 return Long.valueOf(k8sNet.segmentId());
292 }
293
294 private void setNetworkRules(K8sPort port, boolean install) {
295 K8sNetwork k8sNet = k8sNetworkService.network(port.networkId());
296
297 if (k8sNet == null) {
Jian Li2cc2b632019-02-18 00:56:40 +0900298 log.warn("Network {} is not found from port {}.",
299 port.networkId(), port.portId());
Jian Li4aa17642019-01-30 00:01:11 +0900300 return;
301 }
302
303 switch (k8sNet.type()) {
304 case VXLAN:
305 case GRE:
306 case GENEVE:
307 setNetworkRulesForTunnel(port, install);
308 break;
309 default:
Jian Li2cc2b632019-02-18 00:56:40 +0900310 log.warn("The given network type {} is not supported.",
311 k8sNet.type().name());
Jian Li4aa17642019-01-30 00:01:11 +0900312 break;
313 }
314 }
315
316 private String getArpMode() {
317 Set<ConfigProperty> properties =
318 configService.getProperties(K8sSwitchingArpHandler.class.getName());
319 return getPropertyValue(properties, ARP_MODE);
320 }
321
322 private class InternalK8sNetworkListener implements K8sNetworkListener {
323
324 private boolean isRelevantHelper(K8sNetworkEvent event) {
325 return mastershipService.isLocalMaster(event.port().deviceId());
326 }
327
328 @Override
329 public void event(K8sNetworkEvent event) {
330 switch (event.type()) {
331 case K8S_PORT_ACTIVATED:
332 eventExecutor.execute(() -> processInstanceDetection(event));
333 break;
334 case K8S_PORT_REMOVED:
335 eventExecutor.execute(() -> processInstanceRemoval(event));
336 break;
337 default:
338 break;
339 }
340 }
341
342 private void processInstanceDetection(K8sNetworkEvent event) {
343 if (!isRelevantHelper(event)) {
344 return;
345 }
346
347 setNetworkRules(event.port(), true);
348 }
349
350 private void processInstanceRemoval(K8sNetworkEvent event) {
351 if (!isRelevantHelper(event)) {
352 return;
353 }
354
355 setNetworkRules(event.port(), false);
356 }
357 }
358}