blob: fccbaafce02994d5bba632ab9564765a3cb74323 [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())
290 .setArpSpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
291 .setArpSha(k8sNode.extBridgeMac())
292 .setOutput(PortNumber.IN_PORT)
293 .build();
Jian Lieb488ea2019-04-16 01:50:02 +0900294
Jian Li140d8a22019-04-24 23:41:44 +0900295 k8sFlowRuleService.setRule(
296 appId,
297 n.extBridge(),
298 selector,
299 treatment,
300 PRIORITY_STATEFUL_SNAT_RULE,
301 EXT_ENTRY_TABLE,
302 install);
303 });
Jian Lieb488ea2019-04-16 01:50:02 +0900304 }
305
306 private class InternalK8sNodeListener implements K8sNodeListener {
307
308 private boolean isRelevantHelper() {
309 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
310 }
311
312 @Override
313 public void event(K8sNodeEvent event) {
314 switch (event.type()) {
315 case K8S_NODE_COMPLETE:
316 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
317 break;
Jian Li1b08d652019-05-02 17:28:09 +0900318 case K8S_NODE_UPDATED:
319 eventExecutor.execute(() -> processNodeUpdate(event.subject()));
320 break;
Jian Lieb488ea2019-04-16 01:50:02 +0900321 case K8S_NODE_INCOMPLETE:
322 default:
323 break;
324 }
325 }
326
327 private void processNodeCompletion(K8sNode k8sNode) {
328 if (!isRelevantHelper()) {
329 return;
330 }
331
332 setExtIntfArpRule(k8sNode, true);
Jian Lieb488ea2019-04-16 01:50:02 +0900333 setSnatDownstreamRule(k8sNode, true);
334 setContainerToExtRule(k8sNode, true);
335 }
Jian Li1b08d652019-05-02 17:28:09 +0900336
337 private void processNodeUpdate(K8sNode k8sNode) {
338 if (k8sNode.extGatewayMac() != null) {
339 setSnatUpstreamRule(k8sNode, true);
340 }
341 }
Jian Lieb488ea2019-04-16 01:50:02 +0900342 }
343
344 private class InternalK8sNetworkListener implements K8sNetworkListener {
345
346 private boolean isRelevantHelper() {
347 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
348 }
349
350 @Override
351 public void event(K8sNetworkEvent event) {
352 switch (event.type()) {
353 case K8S_PORT_ACTIVATED:
354 eventExecutor.execute(() -> processPortActivation(event.port()));
355 break;
356 case K8S_PORT_REMOVED:
357 eventExecutor.execute(() -> processPortRemoval(event.port()));
358 break;
359 default:
360 break;
361 }
362 }
363
364 private void processPortActivation(K8sPort port) {
365 if (!isRelevantHelper()) {
366 return;
367 }
368
369 k8sNodeService.completeNodes().forEach(n ->
370 setExtToContainerRule(n, port, true));
371 }
372
373 private void processPortRemoval(K8sPort port) {
374 if (!isRelevantHelper()) {
375 return;
376 }
377
378 k8sNodeService.completeNodes().forEach(n ->
379 setExtToContainerRule(n, port, false));
380 }
381 }
382}