blob: f16bddad0b5a21d67b916ec2d42da4414be5b98b [file] [log] [blame]
Jian Lieb488ea2019-04-16 01:50:02 +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 org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpPrefix;
22import org.onlab.packet.TpPort;
23import org.onosproject.cluster.ClusterService;
24import org.onosproject.cluster.LeadershipService;
25import org.onosproject.cluster.NodeId;
26import 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.k8snetworking.util.RulePopulatorUtil;
35import org.onosproject.k8snode.api.K8sNode;
36import org.onosproject.k8snode.api.K8sNodeEvent;
37import org.onosproject.k8snode.api.K8sNodeListener;
38import org.onosproject.k8snode.api.K8sNodeService;
39import org.onosproject.mastership.MastershipService;
40import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.device.DeviceService;
44import org.onosproject.net.driver.DriverService;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.instructions.ExtensionTreatment;
50import org.osgi.service.component.annotations.Activate;
51import org.osgi.service.component.annotations.Component;
52import org.osgi.service.component.annotations.Deactivate;
53import org.osgi.service.component.annotations.Reference;
54import org.osgi.service.component.annotations.ReferenceCardinality;
55import org.slf4j.Logger;
56
57import java.util.Objects;
58import java.util.concurrent.ExecutorService;
59
60import static java.util.concurrent.Executors.newSingleThreadExecutor;
61import static org.onlab.util.Tools.groupedThreads;
62import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Li140d8a22019-04-24 23:41:44 +090063import static org.onosproject.k8snetworking.api.Constants.EXT_ENTRY_TABLE;
Jian Lieb488ea2019-04-16 01:50:02 +090064import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li140d8a22019-04-24 23:41:44 +090065import static org.onosproject.k8snetworking.api.Constants.POD_RESOLUTION_TABLE;
Jian Lieb488ea2019-04-16 01:50:02 +090066import static org.onosproject.k8snetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
67import static org.onosproject.k8snetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
68import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
69import static org.onosproject.k8snetworking.util.RulePopulatorUtil.CT_NAT_SRC_FLAG;
70import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
71import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
72import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
73import static org.slf4j.LoggerFactory.getLogger;
74
75/**
76 * Provides POD's internal to external connectivity using source NAT (SNAT).
77 */
78@Component(immediate = true)
79public class K8sRoutingSnatHandler {
80
81 private final Logger log = getLogger(getClass());
82
Jian Li140d8a22019-04-24 23:41:44 +090083 private static final int HOST_PREFIX = 32;
Jian Lieb488ea2019-04-16 01:50:02 +090084
Jian Li140d8a22019-04-24 23:41:44 +090085 // we try to avoid port number overlapping with node port (30000 ~ 32767)
86 // in case the user has customized node port range, the following static
87 // value should be changed accordingly
88 private static final int TP_PORT_MINIMUM_NUM = 32768;
Jian Lieb488ea2019-04-16 01:50:02 +090089 private static final int TP_PORT_MAXIMUM_NUM = 65535;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected CoreService coreService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected DeviceService deviceService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected DriverService driverService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected LeadershipService leadershipService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected MastershipService mastershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected ClusterService clusterService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected K8sNetworkService k8sNetworkService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected K8sNodeService k8sNodeService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected K8sFlowRuleService k8sFlowRuleService;
117
118 private final InternalK8sNetworkListener k8sNetworkListener =
119 new InternalK8sNetworkListener();
120 private final InternalK8sNodeListener k8sNodeListener =
121 new InternalK8sNodeListener();
122 private final ExecutorService eventExecutor = newSingleThreadExecutor(
123 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
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
132 localNodeId = clusterService.getLocalNode().id();
133 leadershipService.runForLeadership(appId.name());
134 k8sNetworkService.addListener(k8sNetworkListener);
135 k8sNodeService.addListener(k8sNodeListener);
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 setContainerToExtRule(K8sNode k8sNode, boolean install) {
151
152 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
153
154 if (net == null) {
155 return;
156 }
157
158 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
159 .matchEthType(Ethernet.TYPE_IPV4)
160 .matchTunnelId(Long.valueOf(net.segmentId()))
161 .matchEthDst(DEFAULT_GATEWAY_MAC);
162
163 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
164 .setOutput(k8sNode.intgToExtPatchPortNum());
165
166 k8sFlowRuleService.setRule(
167 appId,
168 k8sNode.intgBridge(),
169 sBuilder.build(),
170 tBuilder.build(),
171 PRIORITY_EXTERNAL_ROUTING_RULE,
172 ROUTING_TABLE,
173 install);
174 }
175
176 private void setExtToContainerRule(K8sNode k8sNode,
177 K8sPort k8sPort, boolean install) {
178
179 K8sNetwork net = k8sNetworkService.network(k8sPort.networkId());
180
181 if (net == null) {
182 return;
183 }
184
185 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
186 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li140d8a22019-04-24 23:41:44 +0900187 .matchIPDst(IpPrefix.valueOf(k8sPort.ipAddress(), HOST_PREFIX));
Jian Lieb488ea2019-04-16 01:50:02 +0900188
189 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
190 .setOutput(k8sNode.extToIntgPatchPortNum());
191
192 k8sFlowRuleService.setRule(
193 appId,
194 k8sNode.extBridge(),
195 sBuilder.build(),
196 tBuilder.build(),
197 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900198 POD_RESOLUTION_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900199 install);
200 }
201
202 private void setSnatDownstreamRule(K8sNode k8sNode,
203 boolean install) {
204 DeviceId deviceId = k8sNode.extBridge();
205
Jian Li140d8a22019-04-24 23:41:44 +0900206 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
207 .matchEthType(Ethernet.TYPE_IPV4)
208 .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_PREFIX));
Jian Lieb488ea2019-04-16 01:50:02 +0900209
210 ExtensionTreatment natTreatment = RulePopulatorUtil
211 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
212 .commit(false)
213 .natAction(true)
Jian Li140d8a22019-04-24 23:41:44 +0900214 .table((short) POD_RESOLUTION_TABLE)
Jian Lieb488ea2019-04-16 01:50:02 +0900215 .build();
216
217 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
218 .setEthSrc(DEFAULT_GATEWAY_MAC)
219 .extension(natTreatment, deviceId)
220 .build();
221
222 k8sFlowRuleService.setRule(
223 appId,
224 deviceId,
225 sBuilder.build(),
226 treatment,
227 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900228 EXT_ENTRY_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900229 install);
230 }
231
232 private void setSnatUpstreamRule(K8sNode k8sNode,
233 boolean install) {
234
235 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
236
237 if (net == null) {
238 return;
239 }
240
241 TrafficSelector selector = DefaultTrafficSelector.builder()
242 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li140d8a22019-04-24 23:41:44 +0900243 .matchInPort(k8sNode.extToIntgPatchPortNum())
Jian Lieb488ea2019-04-16 01:50:02 +0900244 .matchEthDst(DEFAULT_GATEWAY_MAC)
245 .build();
246
247 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
248
249 if (install) {
250 ExtensionTreatment natTreatment = RulePopulatorUtil
251 .niciraConnTrackTreatmentBuilder(driverService, k8sNode.extBridge())
252 .commit(true)
253 .natFlag(CT_NAT_SRC_FLAG)
254 .natAction(true)
255 .natIp(k8sNode.extBridgeIp())
256 .natPortMin(TpPort.tpPort(TP_PORT_MINIMUM_NUM))
257 .natPortMax(TpPort.tpPort(TP_PORT_MAXIMUM_NUM))
258 .build();
259
260 tBuilder.extension(natTreatment, k8sNode.extBridge())
261 .setEthSrc(k8sNode.extBridgeMac())
262 .setEthDst(k8sNode.extGatewayMac())
263 .setOutput(k8sNode.extBridgePortNum());
264 }
265
266 k8sFlowRuleService.setRule(
267 appId,
268 k8sNode.extBridge(),
269 selector,
270 tBuilder.build(),
271 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900272 EXT_ENTRY_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900273 install);
274 }
275
276 private void setExtIntfArpRule(K8sNode k8sNode, boolean install) {
Jian Li140d8a22019-04-24 23:41:44 +0900277 k8sNodeService.completeNodes().forEach(n -> {
278 Device device = deviceService.getDevice(n.extBridge());
279 TrafficSelector selector = DefaultTrafficSelector.builder()
280 .matchEthType(Ethernet.TYPE_ARP)
281 .matchArpOp(ARP.OP_REQUEST)
282 .matchArpTpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
283 .build();
Jian Lieb488ea2019-04-16 01:50:02 +0900284
Jian Li140d8a22019-04-24 23:41:44 +0900285 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
286 .setArpOp(ARP.OP_REPLY)
287 .extension(buildMoveEthSrcToDstExtension(device), device.id())
288 .extension(buildMoveArpShaToThaExtension(device), device.id())
289 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
Jian Li1efcb982020-02-04 00:32:21 +0900290 .setEthSrc(k8sNode.extBridgeMac())
Jian Li140d8a22019-04-24 23:41:44 +0900291 .setArpSha(k8sNode.extBridgeMac())
Jian Li1efcb982020-02-04 00:32:21 +0900292 .setArpSpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
Jian Li140d8a22019-04-24 23:41:44 +0900293 .setOutput(PortNumber.IN_PORT)
294 .build();
Jian Lieb488ea2019-04-16 01:50:02 +0900295
Jian Li140d8a22019-04-24 23:41:44 +0900296 k8sFlowRuleService.setRule(
297 appId,
298 n.extBridge(),
299 selector,
300 treatment,
301 PRIORITY_STATEFUL_SNAT_RULE,
302 EXT_ENTRY_TABLE,
303 install);
304 });
Jian Lieb488ea2019-04-16 01:50:02 +0900305 }
306
307 private class InternalK8sNodeListener implements K8sNodeListener {
308
309 private boolean isRelevantHelper() {
310 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
311 }
312
313 @Override
314 public void event(K8sNodeEvent event) {
315 switch (event.type()) {
316 case K8S_NODE_COMPLETE:
317 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
318 break;
Jian Li1b08d652019-05-02 17:28:09 +0900319 case K8S_NODE_UPDATED:
320 eventExecutor.execute(() -> processNodeUpdate(event.subject()));
321 break;
Jian Lieb488ea2019-04-16 01:50:02 +0900322 case K8S_NODE_INCOMPLETE:
323 default:
324 break;
325 }
326 }
327
328 private void processNodeCompletion(K8sNode k8sNode) {
329 if (!isRelevantHelper()) {
330 return;
331 }
332
333 setExtIntfArpRule(k8sNode, true);
Jian Lieb488ea2019-04-16 01:50:02 +0900334 setSnatDownstreamRule(k8sNode, true);
335 setContainerToExtRule(k8sNode, true);
336 }
Jian Li1b08d652019-05-02 17:28:09 +0900337
338 private void processNodeUpdate(K8sNode k8sNode) {
339 if (k8sNode.extGatewayMac() != null) {
340 setSnatUpstreamRule(k8sNode, true);
341 }
342 }
Jian Lieb488ea2019-04-16 01:50:02 +0900343 }
344
345 private class InternalK8sNetworkListener implements K8sNetworkListener {
346
347 private boolean isRelevantHelper() {
348 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
349 }
350
351 @Override
352 public void event(K8sNetworkEvent event) {
353 switch (event.type()) {
354 case K8S_PORT_ACTIVATED:
355 eventExecutor.execute(() -> processPortActivation(event.port()));
356 break;
357 case K8S_PORT_REMOVED:
358 eventExecutor.execute(() -> processPortRemoval(event.port()));
359 break;
360 default:
361 break;
362 }
363 }
364
365 private void processPortActivation(K8sPort port) {
366 if (!isRelevantHelper()) {
367 return;
368 }
369
370 k8sNodeService.completeNodes().forEach(n ->
371 setExtToContainerRule(n, port, true));
372 }
373
374 private void processPortRemoval(K8sPort port) {
375 if (!isRelevantHelper()) {
376 return;
377 }
378
379 k8sNodeService.completeNodes().forEach(n ->
380 setExtToContainerRule(n, port, false));
381 }
382 }
383}