blob: dde682855a47e67c957fdbef65a027fdd1b2d96d [file] [log] [blame]
Jian Li7d111d72019-04-12 13:58:44 +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
Jian Li121ddfe2019-08-27 02:07:05 +090018import org.onlab.packet.ARP;
Jian Li7d111d72019-04-12 13:58:44 +090019import org.onlab.packet.Ethernet;
Jian Li121ddfe2019-08-27 02:07:05 +090020import org.onlab.packet.Ip4Address;
Jian Li7d111d72019-04-12 13:58:44 +090021import org.onlab.packet.IpPrefix;
22import org.onosproject.cluster.ClusterService;
23import org.onosproject.cluster.LeadershipService;
24import org.onosproject.cluster.NodeId;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.k8snetworking.api.K8sFlowRuleService;
28import org.onosproject.k8snetworking.api.K8sNetwork;
29import org.onosproject.k8snetworking.api.K8sNetworkEvent;
30import org.onosproject.k8snetworking.api.K8sNetworkListener;
31import org.onosproject.k8snetworking.api.K8sNetworkService;
32import org.onosproject.k8snode.api.K8sNode;
33import org.onosproject.k8snode.api.K8sNodeEvent;
34import org.onosproject.k8snode.api.K8sNodeListener;
35import org.onosproject.k8snode.api.K8sNodeService;
Jian Li121ddfe2019-08-27 02:07:05 +090036import org.onosproject.net.Device;
Jian Li7d111d72019-04-12 13:58:44 +090037import org.onosproject.net.PortNumber;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.driver.DriverService;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
Jian Li121ddfe2019-08-27 02:07:05 +090044import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li7d111d72019-04-12 13:58:44 +090045import org.onosproject.net.packet.PacketService;
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
53import java.util.Objects;
54import java.util.concurrent.ExecutorService;
55
56import static java.util.concurrent.Executors.newSingleThreadExecutor;
57import static org.onlab.util.Tools.groupedThreads;
Jian Li121ddfe2019-08-27 02:07:05 +090058import static org.onosproject.k8snetworking.api.Constants.B_CLASS;
59import static org.onosproject.k8snetworking.api.Constants.DST;
60import static org.onosproject.k8snetworking.api.Constants.HOST_PREFIX;
Jian Li7d111d72019-04-12 13:58:44 +090061import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li121ddfe2019-08-27 02:07:05 +090062import static org.onosproject.k8snetworking.api.Constants.LOCAL_ENTRY_TABLE;
63import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090064import static org.onosproject.k8snetworking.api.Constants.PRIORITY_GATEWAY_RULE;
Jian Li121ddfe2019-08-27 02:07:05 +090065import static org.onosproject.k8snetworking.api.Constants.PRIORITY_LOCAL_BRIDGE_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090066import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
Jian Li121ddfe2019-08-27 02:07:05 +090067import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
68import static org.onosproject.k8snetworking.api.Constants.SHIFTED_LOCAL_IP_PREFIX;
69import static org.onosproject.k8snetworking.api.Constants.SRC;
Jian Li1ecfbb72020-09-02 14:45:35 +090070import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
Jian Li121ddfe2019-08-27 02:07:05 +090071import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.shiftIpDomain;
Jian Li7d111d72019-04-12 13:58:44 +090072import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
73import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
Jian Li121ddfe2019-08-27 02:07:05 +090074import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
75import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
76import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
77import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
Jian Li7d111d72019-04-12 13:58:44 +090078import static org.slf4j.LoggerFactory.getLogger;
79
80/**
81 * Populates switching flow rules on OVS for providing the connectivity between
82 * container and network gateway.
83 */
84@Component(immediate = true)
85public class K8sSwitchingGatewayHandler {
86
87 private final Logger log = getLogger(getClass());
88
Jian Li121ddfe2019-08-27 02:07:05 +090089 private static final String REQUEST = "req";
90 private static final String REPLY = "rep";
Jian Li7d111d72019-04-12 13:58:44 +090091
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 protected CoreService coreService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected ClusterService clusterService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 protected LeadershipService leadershipService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
102 protected DeviceService deviceService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected DriverService driverService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected PacketService packetService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected K8sFlowRuleService k8sFlowRuleService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected K8sNetworkService k8sNetworkService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected K8sNodeService k8sNodeService;
118
119 private final ExecutorService eventExecutor = newSingleThreadExecutor(
120 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
121 private final InternalK8sNetworkListener k8sNetworkListener =
122 new InternalK8sNetworkListener();
123 private final InternalK8sNodeListener k8sNodeListener =
124 new InternalK8sNodeListener();
125
126 private ApplicationId appId;
127 private NodeId localNodeId;
128
129 @Activate
130 protected void activate() {
131 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
132 k8sNetworkService.addListener(k8sNetworkListener);
133 k8sNodeService.addListener(k8sNodeListener);
134 localNodeId = clusterService.getLocalNode().id();
135 leadershipService.runForLeadership(appId.name());
136
137 log.info("Started");
138 }
139
140 @Deactivate
141 protected void deactivate() {
142 k8sNodeService.removeListener(k8sNodeListener);
143 k8sNetworkService.removeListener(k8sNetworkListener);
144 leadershipService.withdraw(appId.name());
145 eventExecutor.shutdown();
146
147 log.info("Stopped");
148 }
149
150 private void setGatewayRule(K8sNetwork k8sNetwork, boolean install) {
Jian Li7d111d72019-04-12 13:58:44 +0900151 for (K8sNode node : k8sNodeService.completeNodes()) {
Jian Li121ddfe2019-08-27 02:07:05 +0900152 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
153 .matchEthType(Ethernet.TYPE_IPV4)
154 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
155 HOST_PREFIX));
156
Jian Li7d111d72019-04-12 13:58:44 +0900157 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
158
159 if (node.hostname().equals(k8sNetwork.name())) {
Jian Lieb488ea2019-04-16 01:50:02 +0900160 tBuilder.setEthDst(node.intgBridgeMac())
Jian Li4860e372020-09-09 10:23:21 +0900161 .setOutput(node.intgEntryPortNum());
Jian Li7d111d72019-04-12 13:58:44 +0900162 } else {
Jian Li7d111d72019-04-12 13:58:44 +0900163 K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
164
Jian Li1ecfbb72020-09-02 14:45:35 +0900165 tBuilder.setOutput(node.intgToTunPortNum());
166
167 // install flows into tunnel bridge
168 PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
169 k8sNetworkService, node);
170 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
171 .extension(buildExtension(
172 deviceService,
173 node.tunBridge(),
174 localNode.dataIp().getIp4Address()),
175 node.tunBridge())
176 .setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
177 .setOutput(portNum)
178 .build();
179
180 k8sFlowRuleService.setRule(
181 appId,
182 node.tunBridge(),
183 sBuilder.build(),
184 treatmentToRemote,
185 PRIORITY_GATEWAY_RULE,
186 TUN_ENTRY_TABLE,
187 install);
Jian Li7d111d72019-04-12 13:58:44 +0900188 }
189
190 k8sFlowRuleService.setRule(
191 appId,
192 node.intgBridge(),
193 sBuilder.build(),
194 tBuilder.build(),
195 PRIORITY_GATEWAY_RULE,
196 ROUTING_TABLE,
197 install);
Jian Li121ddfe2019-08-27 02:07:05 +0900198
199 if (node.hostname().equals(k8sNetwork.name())) {
200 sBuilder = DefaultTrafficSelector.builder()
Jian Li4860e372020-09-09 10:23:21 +0900201 .matchInPort(node.intgEntryPortNum())
Jian Li121ddfe2019-08-27 02:07:05 +0900202 .matchEthType(Ethernet.TYPE_IPV4)
203 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
204 HOST_PREFIX));
205
206 tBuilder = DefaultTrafficTreatment.builder()
207 .setOutput(node.intgToLocalPatchPortNum());
208
209 k8sFlowRuleService.setRule(
210 appId,
211 node.intgBridge(),
212 sBuilder.build(),
213 tBuilder.build(),
214 PRIORITY_LOCAL_BRIDGE_RULE,
215 ROUTING_TABLE,
216 install);
217 }
Jian Li7d111d72019-04-12 13:58:44 +0900218 }
219 }
220
Jian Li121ddfe2019-08-27 02:07:05 +0900221 private void setLocalBridgeRules(K8sNetwork k8sNetwork, boolean install) {
222 for (K8sNode node : k8sNodeService.completeNodes()) {
223 if (node.hostname().equals(k8sNetwork.name())) {
224 setLocalBridgeRule(k8sNetwork, node, REQUEST, install);
225 setLocalBridgeRule(k8sNetwork, node, REPLY, install);
226 }
227 }
228 }
229
230 private void setLocalBridgeRule(K8sNetwork k8sNetwork, K8sNode k8sNode,
231 String type, boolean install) {
232 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
233 .matchEthType(Ethernet.TYPE_IPV4);
234 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
235
236 ExtensionTreatment loadTreatment = null;
237
238 if (REQUEST.equals(type)) {
239 loadTreatment = buildLoadExtension(deviceService.getDevice(
240 k8sNode.localBridge()), B_CLASS, SRC, SHIFTED_LOCAL_IP_PREFIX);
241 }
242
243 if (REPLY.equals(type)) {
244 loadTreatment = buildLoadExtension(deviceService.getDevice(
245 k8sNode.localBridge()), B_CLASS, DST, SHIFTED_IP_PREFIX);
246 }
247
248 tBuilder.extension(loadTreatment, k8sNode.localBridge());
249
250 if (REQUEST.equals(type)) {
251 sBuilder.matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
252 HOST_PREFIX));
253 tBuilder.setOutput(PortNumber.LOCAL);
254 }
255
256 if (REPLY.equals(type)) {
257 sBuilder.matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
258 HOST_PREFIX));
Jian Li58b33982020-07-01 19:07:02 +0900259 tBuilder.setOutput(k8sNode.localToIntgPatchPortNum());
Jian Li121ddfe2019-08-27 02:07:05 +0900260 }
261
262 k8sFlowRuleService.setRule(
263 appId,
264 k8sNode.localBridge(),
265 sBuilder.build(),
266 tBuilder.build(),
267 PRIORITY_LOCAL_BRIDGE_RULE,
268 LOCAL_ENTRY_TABLE,
269 install);
270 }
271
272 private void setLocalBridgeArpRules(K8sNetwork k8sNetwork, boolean install) {
273 for (K8sNode node : k8sNodeService.completeNodes()) {
274 if (node.hostname().equals(k8sNetwork.name())) {
275 setLocalBridgeArpRule(k8sNetwork, node, install);
276 }
277 }
278 }
279
280 private void setLocalBridgeArpRule(K8sNetwork k8sNetwork, K8sNode k8sNode, boolean install) {
281 Device device = deviceService.getDevice(k8sNode.localBridge());
282
283 String shiftedLocalIp = shiftIpDomain(
284 k8sNetwork.gatewayIp().toString(), SHIFTED_LOCAL_IP_PREFIX);
285
286 TrafficSelector selector = DefaultTrafficSelector.builder()
287 .matchEthType(Ethernet.TYPE_ARP)
288 .matchArpOp(ARP.OP_REQUEST)
289 .matchArpTpa(Ip4Address.valueOf(shiftedLocalIp))
290 .build();
291
292 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
293 .setArpOp(ARP.OP_REPLY)
294 .extension(buildMoveEthSrcToDstExtension(device), device.id())
295 .extension(buildMoveArpShaToThaExtension(device), device.id())
296 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
297 .setArpSpa(Ip4Address.valueOf(shiftedLocalIp))
298 .setArpSha(k8sNode.intgBridgeMac())
299 .setOutput(PortNumber.IN_PORT)
300 .build();
301
302 k8sFlowRuleService.setRule(
303 appId,
304 device.id(),
305 selector,
306 treatment,
307 PRIORITY_ARP_REPLY_RULE,
308 LOCAL_ENTRY_TABLE,
309 install);
310 }
311
Jian Li7d111d72019-04-12 13:58:44 +0900312 private class InternalK8sNetworkListener implements K8sNetworkListener {
313
314 private boolean isRelevantHelper() {
315 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
316 }
317
318 @Override
319 public void event(K8sNetworkEvent event) {
320 switch (event.type()) {
321 case K8S_NETWORK_CREATED:
322 case K8S_NETWORK_UPDATED:
323 eventExecutor.execute(() -> processNetworkCreation(event));
324 break;
325 case K8S_NETWORK_REMOVED:
326 eventExecutor.execute(() -> processNetworkRemoval(event));
327 break;
328 default:
329 break;
330 }
331 }
332
333 private void processNetworkCreation(K8sNetworkEvent event) {
334 if (!isRelevantHelper()) {
335 return;
336 }
337
338 setGatewayRule(event.subject(), true);
Jian Li121ddfe2019-08-27 02:07:05 +0900339 setLocalBridgeRules(event.subject(), true);
340 setLocalBridgeArpRules(event.subject(), true);
Jian Li7d111d72019-04-12 13:58:44 +0900341 }
342
343 private void processNetworkRemoval(K8sNetworkEvent event) {
344 if (!isRelevantHelper()) {
345 return;
346 }
347
348 setGatewayRule(event.subject(), false);
Jian Li121ddfe2019-08-27 02:07:05 +0900349 setLocalBridgeRules(event.subject(), false);
350 setLocalBridgeArpRules(event.subject(), false);
Jian Li7d111d72019-04-12 13:58:44 +0900351 }
352 }
353
354 private class InternalK8sNodeListener implements K8sNodeListener {
355 private boolean isRelevantHelper() {
356 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
357 }
358
359 @Override
360 public void event(K8sNodeEvent event) {
361 switch (event.type()) {
362 case K8S_NODE_COMPLETE:
363 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
364 break;
365 case K8S_NODE_INCOMPLETE:
366 default:
367 break;
368 }
369 }
370
371 private void processNodeCompletion(K8sNode node) {
372 log.info("COMPLETE node {} is detected", node.hostname());
373
374 if (!isRelevantHelper()) {
375 return;
376 }
377
378 k8sNetworkService.networks().forEach(n -> setGatewayRule(n, true));
Jian Li121ddfe2019-08-27 02:07:05 +0900379 k8sNetworkService.networks().forEach(n -> setLocalBridgeRules(n, true));
380 k8sNetworkService.networks().forEach(n -> setLocalBridgeArpRules(n, true));
Jian Li7d111d72019-04-12 13:58:44 +0900381 }
382 }
383}