blob: b0993bf01ef2850079750a0e03307dac1e493f0c [file] [log] [blame]
Jian Li66f6e3c2019-01-25 10:24: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 Li4aa17642019-01-30 00:01:11 +090018import org.onlab.packet.Ethernet;
19import org.onlab.packet.IpPrefix;
Jian Li66f6e3c2019-01-25 10:24:44 +090020import org.onosproject.cluster.ClusterService;
21import org.onosproject.cluster.LeadershipService;
22import org.onosproject.cluster.NodeId;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.k8snetworking.api.K8sFlowRuleService;
Jian Li4aa17642019-01-30 00:01:11 +090026import org.onosproject.k8snetworking.api.K8sNetwork;
27import org.onosproject.k8snetworking.api.K8sNetworkEvent;
28import org.onosproject.k8snetworking.api.K8sNetworkListener;
29import org.onosproject.k8snetworking.api.K8sNetworkService;
Jian Li66f6e3c2019-01-25 10:24:44 +090030import org.onosproject.k8snode.api.K8sNode;
31import org.onosproject.k8snode.api.K8sNodeEvent;
32import org.onosproject.k8snode.api.K8sNodeListener;
33import org.onosproject.k8snode.api.K8sNodeService;
34import org.onosproject.net.DeviceId;
Jian Li4aa17642019-01-30 00:01:11 +090035import org.onosproject.net.PortNumber;
Jian Li66f6e3c2019-01-25 10:24:44 +090036import org.onosproject.net.flow.DefaultFlowRule;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.FlowRule;
40import org.onosproject.net.flow.FlowRuleOperations;
41import org.onosproject.net.flow.FlowRuleOperationsContext;
42import org.onosproject.net.flow.FlowRuleService;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.osgi.service.component.annotations.Activate;
46import org.osgi.service.component.annotations.Component;
47import org.osgi.service.component.annotations.Deactivate;
48import org.osgi.service.component.annotations.Reference;
49import org.osgi.service.component.annotations.ReferenceCardinality;
50import org.slf4j.Logger;
51
52import java.util.Objects;
53import java.util.concurrent.ExecutorService;
54import java.util.concurrent.Executors;
55
56import static org.onlab.util.Tools.groupedThreads;
57import static org.onosproject.k8snetworking.api.Constants.ACL_EGRESS_TABLE;
Jian Li66f6e3c2019-01-25 10:24:44 +090058import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
59import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Li66f6e3c2019-01-25 10:24:44 +090060import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
61import static org.onosproject.k8snetworking.api.Constants.JUMP_TABLE;
62import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
63import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SNAT_RULE;
64import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
65import static org.onosproject.k8snetworking.api.Constants.STAT_INBOUND_TABLE;
66import static org.onosproject.k8snetworking.api.Constants.STAT_OUTBOUND_TABLE;
67import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
68import static org.onosproject.k8snetworking.api.Constants.VTAP_INBOUND_TABLE;
69import static org.onosproject.k8snetworking.api.Constants.VTAP_OUTBOUND_TABLE;
70import static org.slf4j.LoggerFactory.getLogger;
71
72/**
73 * Sets flow rules directly using FlowRuleService.
74 */
75@Component(immediate = true, service = K8sFlowRuleService.class)
76public class K8sFlowRuleManager implements K8sFlowRuleService {
77
78 private final Logger log = getLogger(getClass());
79
80 private static final int DROP_PRIORITY = 0;
81 private static final int HIGH_PRIORITY = 30000;
82 private static final int TIMEOUT_SNAT_RULE = 60;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY)
85 protected FlowRuleService flowRuleService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY)
91 protected ClusterService clusterService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected LeadershipService leadershipService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +090097 protected K8sNetworkService k8sNetworkService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li66f6e3c2019-01-25 10:24:44 +0900100 protected K8sNodeService k8sNodeService;
101
102 private final ExecutorService deviceEventExecutor =
103 Executors.newSingleThreadExecutor(groupedThreads(
104 getClass().getSimpleName(), "device-event"));
Jian Li4aa17642019-01-30 00:01:11 +0900105 private final K8sNetworkListener internalNetworkListener = new InternalK8sNetworkListener();
Jian Li66f6e3c2019-01-25 10:24:44 +0900106 private final K8sNodeListener internalNodeListener = new InternalK8sNodeListener();
107
108 private ApplicationId appId;
109 private NodeId localNodeId;
110
111 @Activate
112 protected void activate() {
113 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
114 coreService.registerApplication(K8S_NETWORKING_APP_ID);
115 k8sNodeService.addListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900116 k8sNetworkService.addListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900117 localNodeId = clusterService.getLocalNode().id();
118 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900119 k8sNodeService.completeNodes().forEach(this::initializePipeline);
Jian Li66f6e3c2019-01-25 10:24:44 +0900120
121 log.info("Started");
122 }
123
124 @Deactivate
125 protected void deactivate() {
126 k8sNodeService.removeListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900127 k8sNetworkService.removeListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900128 leadershipService.withdraw(appId.name());
129 deviceEventExecutor.shutdown();
130
131 log.info("Stopped");
132 }
133
134 @Override
135 public void setRule(ApplicationId appId, DeviceId deviceId,
136 TrafficSelector selector, TrafficTreatment treatment,
137 int priority, int tableType, boolean install) {
138 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder()
139 .forDevice(deviceId)
140 .withSelector(selector)
141 .withTreatment(treatment)
142 .withPriority(priority)
143 .fromApp(appId)
144 .forTable(tableType);
145
146 if (priority == PRIORITY_SNAT_RULE) {
147 flowRuleBuilder.makeTemporary(TIMEOUT_SNAT_RULE);
148 } else {
149 flowRuleBuilder.makePermanent();
150 }
151
152 applyRule(flowRuleBuilder.build(), install);
153 }
154
155 @Override
156 public void setUpTableMissEntry(DeviceId deviceId, int table) {
157 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
158 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
159
160 treatment.drop();
161
162 FlowRule flowRule = DefaultFlowRule.builder()
163 .forDevice(deviceId)
164 .withSelector(selector.build())
165 .withTreatment(treatment.build())
166 .withPriority(DROP_PRIORITY)
167 .fromApp(appId)
168 .makePermanent()
169 .forTable(table)
170 .build();
171
172 applyRule(flowRule, true);
173 }
174
175 @Override
176 public void connectTables(DeviceId deviceId, int fromTable, int toTable) {
177 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
178 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
179
180 treatment.transition(toTable);
181
182 FlowRule flowRule = DefaultFlowRule.builder()
183 .forDevice(deviceId)
184 .withSelector(selector.build())
185 .withTreatment(treatment.build())
186 .withPriority(DROP_PRIORITY)
187 .fromApp(appId)
188 .makePermanent()
189 .forTable(fromTable)
190 .build();
191
192 applyRule(flowRule, true);
193 }
194
195 private void applyRule(FlowRule flowRule, boolean install) {
196 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
197
198 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
199
200 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
201 @Override
202 public void onSuccess(FlowRuleOperations ops) {
203 log.debug("Provisioned vni or forwarding table");
204 }
205
206 @Override
207 public void onError(FlowRuleOperations ops) {
208 log.debug("Failed to provision vni or forwarding table");
209 }
210 }));
211 }
212
Jian Li4aa17642019-01-30 00:01:11 +0900213 protected void initializePipeline(K8sNode k8sNode) {
214
215 DeviceId deviceId = k8sNode.intgBridge();
216
Jian Li66f6e3c2019-01-25 10:24:44 +0900217 // for inbound table transition
218 connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
Jian Lid89db462019-02-08 18:21:57 +0900219 connectTables(deviceId, VTAP_INBOUND_TABLE, VTAG_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900220
221 // for vTag and ARP table transition
222 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
223
Jian Li66f6e3c2019-01-25 10:24:44 +0900224 connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
225
Jian Li4aa17642019-01-30 00:01:11 +0900226 // for ARP and ACL table transition
227 connectTables(deviceId, ARP_TABLE, JUMP_TABLE);
228
Jian Li66f6e3c2019-01-25 10:24:44 +0900229 // for JUMP table transition
230 // we need JUMP table for bypassing routing table which contains large
231 // amount of flow rules which might cause performance degradation during
232 // table lookup
Jian Li4aa17642019-01-30 00:01:11 +0900233 setupJumpTable(k8sNode);
Jian Li66f6e3c2019-01-25 10:24:44 +0900234
235 // for outbound table transition
236 connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
237 connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
238 }
239
Jian Li4aa17642019-01-30 00:01:11 +0900240 private void setupJumpTable(K8sNode k8sNode) {
241 DeviceId deviceId = k8sNode.intgBridge();
242
Jian Li66f6e3c2019-01-25 10:24:44 +0900243 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
244 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
245
246 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
247 treatment.transition(ROUTING_TABLE);
248
249 FlowRule flowRule = DefaultFlowRule.builder()
250 .forDevice(deviceId)
251 .withSelector(selector.build())
252 .withTreatment(treatment.build())
253 .withPriority(HIGH_PRIORITY)
254 .fromApp(appId)
255 .makePermanent()
256 .forTable(JUMP_TABLE)
257 .build();
258
259 applyRule(flowRule, true);
260
261 selector = DefaultTrafficSelector.builder();
262 treatment = DefaultTrafficTreatment.builder();
263
264 treatment.transition(STAT_OUTBOUND_TABLE);
265
266 flowRule = DefaultFlowRule.builder()
267 .forDevice(deviceId)
268 .withSelector(selector.build())
269 .withTreatment(treatment.build())
270 .withPriority(DROP_PRIORITY)
271 .fromApp(appId)
272 .makePermanent()
273 .forTable(JUMP_TABLE)
274 .build();
275
276 applyRule(flowRule, true);
277 }
278
Jian Li4aa17642019-01-30 00:01:11 +0900279 private void setupHostGwRule(K8sNetwork k8sNetwork) {
280 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
281 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
282 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32));
283
284 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
285 tBuilder.setOutput(PortNumber.LOCAL);
286
287 for (K8sNode node : k8sNodeService.completeNodes()) {
288 FlowRule flowRule = DefaultFlowRule.builder()
289 .forDevice(node.intgBridge())
290 .withSelector(sBuilder.build())
291 .withTreatment(tBuilder.build())
292 .withPriority(HIGH_PRIORITY)
293 .fromApp(appId)
294 .makePermanent()
295 .forTable(JUMP_TABLE)
296 .build();
297 applyRule(flowRule, true);
298 }
299
300 sBuilder = DefaultTrafficSelector.builder();
301 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
302 .matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32))
303 .matchIPDst(IpPrefix.valueOf(k8sNetwork.cidr()));
304
305 tBuilder = DefaultTrafficTreatment.builder();
306 tBuilder.setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
307 .transition(STAT_OUTBOUND_TABLE);
308
309 for (K8sNode node : k8sNodeService.completeNodes()) {
310 FlowRule flowRule = DefaultFlowRule.builder()
311 .forDevice(node.intgBridge())
312 .withSelector(sBuilder.build())
313 .withTreatment(tBuilder.build())
314 .withPriority(HIGH_PRIORITY)
315 .fromApp(appId)
316 .makePermanent()
317 .forTable(JUMP_TABLE)
318 .build();
319 applyRule(flowRule, true);
320 }
321 }
322
Jian Li66f6e3c2019-01-25 10:24:44 +0900323 private class InternalK8sNodeListener implements K8sNodeListener {
324 private boolean isRelevantHelper() {
325 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
326 }
327
328 @Override
329 public void event(K8sNodeEvent event) {
330 K8sNode k8sNode = event.subject();
331
332 switch (event.type()) {
333 case K8S_NODE_COMPLETE:
334 deviceEventExecutor.execute(() -> {
335 log.info("COMPLETE node {} is detected", k8sNode.hostname());
336
337 if (!isRelevantHelper()) {
338 return;
339 }
340
Jian Li4aa17642019-01-30 00:01:11 +0900341 initializePipeline(k8sNode);
Jian Li66f6e3c2019-01-25 10:24:44 +0900342 });
343 break;
344 case K8S_NODE_CREATED:
345 default:
346 // do nothing
347 break;
348 }
349 }
350 }
Jian Li4aa17642019-01-30 00:01:11 +0900351
352 private class InternalK8sNetworkListener implements K8sNetworkListener {
353
354 private boolean isRelevantHelper() {
355 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
356 }
357
358 @Override
359 public void event(K8sNetworkEvent event) {
360
361 switch (event.type()) {
362 case K8S_NETWORK_CREATED:
363 deviceEventExecutor.execute(() -> processNetworkCreation(event.subject()));
364 break;
365 case K8S_NETWORK_REMOVED:
366 break;
367 default:
368 break;
369 }
370 }
371
372 private void processNetworkCreation(K8sNetwork network) {
373 if (!isRelevantHelper()) {
374 return;
375 }
376
377 setupHostGwRule(network);
378 }
379 }
Jian Li66f6e3c2019-01-25 10:24:44 +0900380}