blob: 2f994da4d930c62a6a849f0bc6a094356d77b436 [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 Li1a2eb5d2019-08-27 02:07:05 +090018import org.onlab.packet.ARP;
Jian Li7d111d72019-04-12 13:58:44 +090019import org.onlab.packet.Ethernet;
Jian Li1a2eb5d2019-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 Li1a2eb5d2019-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 Li1a2eb5d2019-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 Li1a2eb5d2019-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 Li1a2eb5d2019-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 Li1a2eb5d2019-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 Li1a2eb5d2019-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;
70import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.shiftIpDomain;
Jian Li7d111d72019-04-12 13:58:44 +090071import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
72import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
Jian Li1a2eb5d2019-08-27 02:07:05 +090073import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildLoadExtension;
74import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
75import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
76import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
Jian Li7d111d72019-04-12 13:58:44 +090077import static org.slf4j.LoggerFactory.getLogger;
78
79/**
80 * Populates switching flow rules on OVS for providing the connectivity between
81 * container and network gateway.
82 */
83@Component(immediate = true)
84public class K8sSwitchingGatewayHandler {
85
86 private final Logger log = getLogger(getClass());
87
Jian Li1a2eb5d2019-08-27 02:07:05 +090088 private static final String REQUEST = "req";
89 private static final String REPLY = "rep";
Jian Li7d111d72019-04-12 13:58:44 +090090
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected CoreService coreService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected ClusterService clusterService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected LeadershipService leadershipService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected DeviceService deviceService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected DriverService driverService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected PacketService packetService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected K8sFlowRuleService k8sFlowRuleService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected K8sNetworkService k8sNetworkService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected K8sNodeService k8sNodeService;
117
118 private final ExecutorService eventExecutor = newSingleThreadExecutor(
119 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
120 private final InternalK8sNetworkListener k8sNetworkListener =
121 new InternalK8sNetworkListener();
122 private final InternalK8sNodeListener k8sNodeListener =
123 new InternalK8sNodeListener();
124
125 private ApplicationId appId;
126 private NodeId localNodeId;
127
128 @Activate
129 protected void activate() {
130 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
131 k8sNetworkService.addListener(k8sNetworkListener);
132 k8sNodeService.addListener(k8sNodeListener);
133 localNodeId = clusterService.getLocalNode().id();
134 leadershipService.runForLeadership(appId.name());
135
136 log.info("Started");
137 }
138
139 @Deactivate
140 protected void deactivate() {
141 k8sNodeService.removeListener(k8sNodeListener);
142 k8sNetworkService.removeListener(k8sNetworkListener);
143 leadershipService.withdraw(appId.name());
144 eventExecutor.shutdown();
145
146 log.info("Stopped");
147 }
148
149 private void setGatewayRule(K8sNetwork k8sNetwork, boolean install) {
Jian Li7d111d72019-04-12 13:58:44 +0900150 for (K8sNode node : k8sNodeService.completeNodes()) {
Jian Li1a2eb5d2019-08-27 02:07:05 +0900151 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
152 .matchEthType(Ethernet.TYPE_IPV4)
153 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
154 HOST_PREFIX));
155
Jian Li7d111d72019-04-12 13:58:44 +0900156 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
157
158 if (node.hostname().equals(k8sNetwork.name())) {
Jian Lieb488ea2019-04-16 01:50:02 +0900159 tBuilder.setEthDst(node.intgBridgeMac())
Jian Li7d111d72019-04-12 13:58:44 +0900160 .setOutput(PortNumber.LOCAL);
161 } else {
162 PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
163 k8sNetworkService, node);
164 K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
165
166 tBuilder.extension(buildExtension(
167 deviceService,
168 node.intgBridge(),
169 localNode.dataIp().getIp4Address()),
170 node.intgBridge())
171 .setOutput(portNum);
172 }
173
174 k8sFlowRuleService.setRule(
175 appId,
176 node.intgBridge(),
177 sBuilder.build(),
178 tBuilder.build(),
179 PRIORITY_GATEWAY_RULE,
180 ROUTING_TABLE,
181 install);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900182
183 if (node.hostname().equals(k8sNetwork.name())) {
184 sBuilder = DefaultTrafficSelector.builder()
185 .matchInPort(PortNumber.LOCAL)
186 .matchEthType(Ethernet.TYPE_IPV4)
187 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
188 HOST_PREFIX));
189
190 tBuilder = DefaultTrafficTreatment.builder()
191 .setOutput(node.intgToLocalPatchPortNum());
192
193 k8sFlowRuleService.setRule(
194 appId,
195 node.intgBridge(),
196 sBuilder.build(),
197 tBuilder.build(),
198 PRIORITY_LOCAL_BRIDGE_RULE,
199 ROUTING_TABLE,
200 install);
201 }
Jian Li7d111d72019-04-12 13:58:44 +0900202 }
203 }
204
Jian Li1a2eb5d2019-08-27 02:07:05 +0900205 private void setLocalBridgeRules(K8sNetwork k8sNetwork, boolean install) {
206 for (K8sNode node : k8sNodeService.completeNodes()) {
207 if (node.hostname().equals(k8sNetwork.name())) {
208 setLocalBridgeRule(k8sNetwork, node, REQUEST, install);
209 setLocalBridgeRule(k8sNetwork, node, REPLY, install);
210 }
211 }
212 }
213
214 private void setLocalBridgeRule(K8sNetwork k8sNetwork, K8sNode k8sNode,
215 String type, boolean install) {
216 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
217 .matchEthType(Ethernet.TYPE_IPV4);
218 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
219
220 ExtensionTreatment loadTreatment = null;
221
222 if (REQUEST.equals(type)) {
223 loadTreatment = buildLoadExtension(deviceService.getDevice(
224 k8sNode.localBridge()), B_CLASS, SRC, SHIFTED_LOCAL_IP_PREFIX);
225 }
226
227 if (REPLY.equals(type)) {
228 loadTreatment = buildLoadExtension(deviceService.getDevice(
229 k8sNode.localBridge()), B_CLASS, DST, SHIFTED_IP_PREFIX);
230 }
231
232 tBuilder.extension(loadTreatment, k8sNode.localBridge());
233
234 if (REQUEST.equals(type)) {
235 sBuilder.matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
236 HOST_PREFIX));
237 tBuilder.setOutput(PortNumber.LOCAL);
238 }
239
240 if (REPLY.equals(type)) {
241 sBuilder.matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
242 HOST_PREFIX));
Jian Lie2a04ce2020-07-01 19:07:02 +0900243 tBuilder.setOutput(k8sNode.localToIntgPatchPortNum());
Jian Li1a2eb5d2019-08-27 02:07:05 +0900244 }
245
246 k8sFlowRuleService.setRule(
247 appId,
248 k8sNode.localBridge(),
249 sBuilder.build(),
250 tBuilder.build(),
251 PRIORITY_LOCAL_BRIDGE_RULE,
252 LOCAL_ENTRY_TABLE,
253 install);
254 }
255
256 private void setLocalBridgeArpRules(K8sNetwork k8sNetwork, boolean install) {
257 for (K8sNode node : k8sNodeService.completeNodes()) {
258 if (node.hostname().equals(k8sNetwork.name())) {
259 setLocalBridgeArpRule(k8sNetwork, node, install);
260 }
261 }
262 }
263
264 private void setLocalBridgeArpRule(K8sNetwork k8sNetwork, K8sNode k8sNode, boolean install) {
265 Device device = deviceService.getDevice(k8sNode.localBridge());
266
267 String shiftedLocalIp = shiftIpDomain(
268 k8sNetwork.gatewayIp().toString(), SHIFTED_LOCAL_IP_PREFIX);
269
270 TrafficSelector selector = DefaultTrafficSelector.builder()
271 .matchEthType(Ethernet.TYPE_ARP)
272 .matchArpOp(ARP.OP_REQUEST)
273 .matchArpTpa(Ip4Address.valueOf(shiftedLocalIp))
274 .build();
275
276 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
277 .setArpOp(ARP.OP_REPLY)
278 .extension(buildMoveEthSrcToDstExtension(device), device.id())
279 .extension(buildMoveArpShaToThaExtension(device), device.id())
280 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
281 .setArpSpa(Ip4Address.valueOf(shiftedLocalIp))
282 .setArpSha(k8sNode.intgBridgeMac())
283 .setOutput(PortNumber.IN_PORT)
284 .build();
285
286 k8sFlowRuleService.setRule(
287 appId,
288 device.id(),
289 selector,
290 treatment,
291 PRIORITY_ARP_REPLY_RULE,
292 LOCAL_ENTRY_TABLE,
293 install);
294 }
295
Jian Li7d111d72019-04-12 13:58:44 +0900296 private class InternalK8sNetworkListener implements K8sNetworkListener {
297
298 private boolean isRelevantHelper() {
299 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
300 }
301
302 @Override
303 public void event(K8sNetworkEvent event) {
304 switch (event.type()) {
305 case K8S_NETWORK_CREATED:
306 case K8S_NETWORK_UPDATED:
307 eventExecutor.execute(() -> processNetworkCreation(event));
308 break;
309 case K8S_NETWORK_REMOVED:
310 eventExecutor.execute(() -> processNetworkRemoval(event));
311 break;
312 default:
313 break;
314 }
315 }
316
317 private void processNetworkCreation(K8sNetworkEvent event) {
318 if (!isRelevantHelper()) {
319 return;
320 }
321
322 setGatewayRule(event.subject(), true);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900323 setLocalBridgeRules(event.subject(), true);
324 setLocalBridgeArpRules(event.subject(), true);
Jian Li7d111d72019-04-12 13:58:44 +0900325 }
326
327 private void processNetworkRemoval(K8sNetworkEvent event) {
328 if (!isRelevantHelper()) {
329 return;
330 }
331
332 setGatewayRule(event.subject(), false);
Jian Li1a2eb5d2019-08-27 02:07:05 +0900333 setLocalBridgeRules(event.subject(), false);
334 setLocalBridgeArpRules(event.subject(), false);
Jian Li7d111d72019-04-12 13:58:44 +0900335 }
336 }
337
338 private class InternalK8sNodeListener implements K8sNodeListener {
339 private boolean isRelevantHelper() {
340 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
341 }
342
343 @Override
344 public void event(K8sNodeEvent event) {
345 switch (event.type()) {
346 case K8S_NODE_COMPLETE:
347 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
348 break;
349 case K8S_NODE_INCOMPLETE:
350 default:
351 break;
352 }
353 }
354
355 private void processNodeCompletion(K8sNode node) {
356 log.info("COMPLETE node {} is detected", node.hostname());
357
358 if (!isRelevantHelper()) {
359 return;
360 }
361
362 k8sNetworkService.networks().forEach(n -> setGatewayRule(n, true));
Jian Li1a2eb5d2019-08-27 02:07:05 +0900363 k8sNetworkService.networks().forEach(n -> setLocalBridgeRules(n, true));
364 k8sNetworkService.networks().forEach(n -> setLocalBridgeArpRules(n, true));
Jian Li7d111d72019-04-12 13:58:44 +0900365 }
366 }
367}