blob: 5fd2758d7a85598477f5faf2d21f135e57926c8a [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;
Jian Lib1218442020-09-03 13:12:14 +090022import org.onlab.packet.MacAddress;
Jian Lieb488ea2019-04-16 01:50:02 +090023import org.onlab.packet.TpPort;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.LeadershipService;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.k8snetworking.api.K8sFlowRuleService;
30import org.onosproject.k8snetworking.api.K8sNetwork;
31import org.onosproject.k8snetworking.api.K8sNetworkEvent;
32import org.onosproject.k8snetworking.api.K8sNetworkListener;
33import org.onosproject.k8snetworking.api.K8sNetworkService;
34import org.onosproject.k8snetworking.api.K8sPort;
35import org.onosproject.k8snetworking.util.RulePopulatorUtil;
36import org.onosproject.k8snode.api.K8sNode;
37import org.onosproject.k8snode.api.K8sNodeEvent;
38import org.onosproject.k8snode.api.K8sNodeListener;
39import org.onosproject.k8snode.api.K8sNodeService;
40import org.onosproject.mastership.MastershipService;
41import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
43import org.onosproject.net.PortNumber;
44import org.onosproject.net.device.DeviceService;
45import org.onosproject.net.driver.DriverService;
46import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
50import org.onosproject.net.flow.instructions.ExtensionTreatment;
51import org.osgi.service.component.annotations.Activate;
52import org.osgi.service.component.annotations.Component;
53import org.osgi.service.component.annotations.Deactivate;
54import org.osgi.service.component.annotations.Reference;
55import org.osgi.service.component.annotations.ReferenceCardinality;
56import org.slf4j.Logger;
57
58import java.util.Objects;
59import java.util.concurrent.ExecutorService;
60
61import static java.util.concurrent.Executors.newSingleThreadExecutor;
62import static org.onlab.util.Tools.groupedThreads;
63import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Li140d8a22019-04-24 23:41:44 +090064import static org.onosproject.k8snetworking.api.Constants.EXT_ENTRY_TABLE;
Jian Lieb488ea2019-04-16 01:50:02 +090065import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li140d8a22019-04-24 23:41:44 +090066import static org.onosproject.k8snetworking.api.Constants.POD_RESOLUTION_TABLE;
Jian Lieb488ea2019-04-16 01:50:02 +090067import static org.onosproject.k8snetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
68import static org.onosproject.k8snetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
69import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
70import static org.onosproject.k8snetworking.util.RulePopulatorUtil.CT_NAT_SRC_FLAG;
71import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
72import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
73import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
Jian Lib1218442020-09-03 13:12:14 +090074import static org.onosproject.k8snode.api.Constants.DEFAULT_EXTERNAL_GATEWAY_MAC;
Jian Lieb488ea2019-04-16 01:50:02 +090075import static org.slf4j.LoggerFactory.getLogger;
76
77/**
78 * Provides POD's internal to external connectivity using source NAT (SNAT).
79 */
80@Component(immediate = true)
81public class K8sRoutingSnatHandler {
82
83 private final Logger log = getLogger(getClass());
84
Jian Li140d8a22019-04-24 23:41:44 +090085 private static final int HOST_PREFIX = 32;
Jian Lieb488ea2019-04-16 01:50:02 +090086
Jian Li140d8a22019-04-24 23:41:44 +090087 // we try to avoid port number overlapping with node port (30000 ~ 32767)
88 // in case the user has customized node port range, the following static
89 // value should be changed accordingly
90 private static final int TP_PORT_MINIMUM_NUM = 32768;
Jian Lieb488ea2019-04-16 01:50:02 +090091 private static final int TP_PORT_MAXIMUM_NUM = 65535;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected CoreService coreService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected DeviceService deviceService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected DriverService driverService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected LeadershipService leadershipService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected MastershipService mastershipService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected ClusterService clusterService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected K8sNetworkService k8sNetworkService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected K8sNodeService k8sNodeService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
118 protected K8sFlowRuleService k8sFlowRuleService;
119
120 private final InternalK8sNetworkListener k8sNetworkListener =
121 new InternalK8sNetworkListener();
122 private final InternalK8sNodeListener k8sNodeListener =
123 new InternalK8sNodeListener();
124 private final ExecutorService eventExecutor = newSingleThreadExecutor(
125 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
126
127 private ApplicationId appId;
128 private NodeId localNodeId;
129
130 @Activate
131 protected void activate() {
132 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
133
134 localNodeId = clusterService.getLocalNode().id();
135 leadershipService.runForLeadership(appId.name());
136 k8sNetworkService.addListener(k8sNetworkListener);
137 k8sNodeService.addListener(k8sNodeListener);
138
139 log.info("Started");
140 }
141
142 @Deactivate
143 protected void deactivate() {
144 k8sNodeService.removeListener(k8sNodeListener);
145 k8sNetworkService.removeListener(k8sNetworkListener);
146 leadershipService.withdraw(appId.name());
147 eventExecutor.shutdown();
148
149 log.info("Stopped");
150 }
151
152 private void setContainerToExtRule(K8sNode k8sNode, boolean install) {
153
154 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
155
156 if (net == null) {
157 return;
158 }
159
160 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
161 .matchEthType(Ethernet.TYPE_IPV4)
162 .matchTunnelId(Long.valueOf(net.segmentId()))
163 .matchEthDst(DEFAULT_GATEWAY_MAC);
164
165 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
166 .setOutput(k8sNode.intgToExtPatchPortNum());
167
168 k8sFlowRuleService.setRule(
169 appId,
170 k8sNode.intgBridge(),
171 sBuilder.build(),
172 tBuilder.build(),
173 PRIORITY_EXTERNAL_ROUTING_RULE,
174 ROUTING_TABLE,
175 install);
176 }
177
178 private void setExtToContainerRule(K8sNode k8sNode,
179 K8sPort k8sPort, boolean install) {
180
181 K8sNetwork net = k8sNetworkService.network(k8sPort.networkId());
182
183 if (net == null) {
184 return;
185 }
186
187 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
188 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li140d8a22019-04-24 23:41:44 +0900189 .matchIPDst(IpPrefix.valueOf(k8sPort.ipAddress(), HOST_PREFIX));
Jian Lieb488ea2019-04-16 01:50:02 +0900190
191 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
192 .setOutput(k8sNode.extToIntgPatchPortNum());
193
194 k8sFlowRuleService.setRule(
195 appId,
196 k8sNode.extBridge(),
197 sBuilder.build(),
198 tBuilder.build(),
199 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900200 POD_RESOLUTION_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900201 install);
202 }
203
204 private void setSnatDownstreamRule(K8sNode k8sNode,
205 boolean install) {
206 DeviceId deviceId = k8sNode.extBridge();
207
Jian Li140d8a22019-04-24 23:41:44 +0900208 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
209 .matchEthType(Ethernet.TYPE_IPV4)
210 .matchIPDst(IpPrefix.valueOf(k8sNode.extBridgeIp(), HOST_PREFIX));
Jian Lieb488ea2019-04-16 01:50:02 +0900211
212 ExtensionTreatment natTreatment = RulePopulatorUtil
213 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
214 .commit(false)
215 .natAction(true)
Jian Li140d8a22019-04-24 23:41:44 +0900216 .table((short) POD_RESOLUTION_TABLE)
Jian Lieb488ea2019-04-16 01:50:02 +0900217 .build();
218
219 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
220 .setEthSrc(DEFAULT_GATEWAY_MAC)
221 .extension(natTreatment, deviceId)
222 .build();
223
224 k8sFlowRuleService.setRule(
225 appId,
226 deviceId,
227 sBuilder.build(),
228 treatment,
229 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900230 EXT_ENTRY_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900231 install);
232 }
233
234 private void setSnatUpstreamRule(K8sNode k8sNode,
235 boolean install) {
236
237 K8sNetwork net = k8sNetworkService.network(k8sNode.hostname());
238
239 if (net == null) {
240 return;
241 }
242
243 TrafficSelector selector = DefaultTrafficSelector.builder()
244 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li140d8a22019-04-24 23:41:44 +0900245 .matchInPort(k8sNode.extToIntgPatchPortNum())
Jian Lieb488ea2019-04-16 01:50:02 +0900246 .matchEthDst(DEFAULT_GATEWAY_MAC)
247 .build();
248
249 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
250
251 if (install) {
252 ExtensionTreatment natTreatment = RulePopulatorUtil
253 .niciraConnTrackTreatmentBuilder(driverService, k8sNode.extBridge())
254 .commit(true)
255 .natFlag(CT_NAT_SRC_FLAG)
256 .natAction(true)
257 .natIp(k8sNode.extBridgeIp())
258 .natPortMin(TpPort.tpPort(TP_PORT_MINIMUM_NUM))
259 .natPortMax(TpPort.tpPort(TP_PORT_MAXIMUM_NUM))
260 .build();
261
262 tBuilder.extension(natTreatment, k8sNode.extBridge())
263 .setEthSrc(k8sNode.extBridgeMac())
Jian Lib1218442020-09-03 13:12:14 +0900264 .setEthDst(k8sNode.extGatewayMac());
265
266 if (MacAddress.valueOf(DEFAULT_EXTERNAL_GATEWAY_MAC).equals(
267 k8sNode.extGatewayMac())) {
268 tBuilder.setOutput(k8sNode.extIntfPortNum());
269 } else {
270 tBuilder.setOutput(k8sNode.extBridgePortNum());
271 }
Jian Lieb488ea2019-04-16 01:50:02 +0900272 }
273
274 k8sFlowRuleService.setRule(
275 appId,
276 k8sNode.extBridge(),
277 selector,
278 tBuilder.build(),
279 PRIORITY_STATEFUL_SNAT_RULE,
Jian Li140d8a22019-04-24 23:41:44 +0900280 EXT_ENTRY_TABLE,
Jian Lieb488ea2019-04-16 01:50:02 +0900281 install);
282 }
283
284 private void setExtIntfArpRule(K8sNode k8sNode, boolean install) {
Jian Li140d8a22019-04-24 23:41:44 +0900285 k8sNodeService.completeNodes().forEach(n -> {
286 Device device = deviceService.getDevice(n.extBridge());
287 TrafficSelector selector = DefaultTrafficSelector.builder()
288 .matchEthType(Ethernet.TYPE_ARP)
289 .matchArpOp(ARP.OP_REQUEST)
290 .matchArpTpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
291 .build();
Jian Lieb488ea2019-04-16 01:50:02 +0900292
Jian Li140d8a22019-04-24 23:41:44 +0900293 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
294 .setArpOp(ARP.OP_REPLY)
295 .extension(buildMoveEthSrcToDstExtension(device), device.id())
296 .extension(buildMoveArpShaToThaExtension(device), device.id())
297 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
Jian Li232a32c2020-02-04 00:32:21 +0900298 .setEthSrc(k8sNode.extBridgeMac())
Jian Li140d8a22019-04-24 23:41:44 +0900299 .setArpSha(k8sNode.extBridgeMac())
Jian Li232a32c2020-02-04 00:32:21 +0900300 .setArpSpa(Ip4Address.valueOf(k8sNode.extBridgeIp().toString()))
Jian Li140d8a22019-04-24 23:41:44 +0900301 .setOutput(PortNumber.IN_PORT)
302 .build();
Jian Lieb488ea2019-04-16 01:50:02 +0900303
Jian Li140d8a22019-04-24 23:41:44 +0900304 k8sFlowRuleService.setRule(
305 appId,
306 n.extBridge(),
307 selector,
308 treatment,
309 PRIORITY_STATEFUL_SNAT_RULE,
310 EXT_ENTRY_TABLE,
311 install);
312 });
Jian Lieb488ea2019-04-16 01:50:02 +0900313 }
314
315 private class InternalK8sNodeListener implements K8sNodeListener {
316
317 private boolean isRelevantHelper() {
318 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
319 }
320
321 @Override
322 public void event(K8sNodeEvent event) {
323 switch (event.type()) {
324 case K8S_NODE_COMPLETE:
325 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
326 break;
Jian Li1b08d652019-05-02 17:28:09 +0900327 case K8S_NODE_UPDATED:
328 eventExecutor.execute(() -> processNodeUpdate(event.subject()));
329 break;
Jian Lieb488ea2019-04-16 01:50:02 +0900330 case K8S_NODE_INCOMPLETE:
331 default:
332 break;
333 }
334 }
335
336 private void processNodeCompletion(K8sNode k8sNode) {
337 if (!isRelevantHelper()) {
338 return;
339 }
340
341 setExtIntfArpRule(k8sNode, true);
Jian Lieb488ea2019-04-16 01:50:02 +0900342 setSnatDownstreamRule(k8sNode, true);
343 setContainerToExtRule(k8sNode, true);
344 }
Jian Li1b08d652019-05-02 17:28:09 +0900345
346 private void processNodeUpdate(K8sNode k8sNode) {
347 if (k8sNode.extGatewayMac() != null) {
348 setSnatUpstreamRule(k8sNode, true);
349 }
350 }
Jian Lieb488ea2019-04-16 01:50:02 +0900351 }
352
353 private class InternalK8sNetworkListener implements K8sNetworkListener {
354
355 private boolean isRelevantHelper() {
356 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
357 }
358
359 @Override
360 public void event(K8sNetworkEvent event) {
361 switch (event.type()) {
362 case K8S_PORT_ACTIVATED:
363 eventExecutor.execute(() -> processPortActivation(event.port()));
364 break;
365 case K8S_PORT_REMOVED:
366 eventExecutor.execute(() -> processPortRemoval(event.port()));
367 break;
368 default:
369 break;
370 }
371 }
372
373 private void processPortActivation(K8sPort port) {
374 if (!isRelevantHelper()) {
375 return;
376 }
377
378 k8sNodeService.completeNodes().forEach(n ->
379 setExtToContainerRule(n, port, true));
380 }
381
382 private void processPortRemoval(K8sPort port) {
383 if (!isRelevantHelper()) {
384 return;
385 }
386
387 k8sNodeService.completeNodes().forEach(n ->
388 setExtToContainerRule(n, port, false));
389 }
390 }
391}