blob: 25d8a939a937e15b7ddf92f06cb14abc8a2c5b91 [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 Li7d111d72019-04-12 13:58:44 +090020import org.onlab.packet.MacAddress;
Jian Li66f6e3c2019-01-25 10:24:44 +090021import org.onosproject.cluster.ClusterService;
22import org.onosproject.cluster.LeadershipService;
23import org.onosproject.cluster.NodeId;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.k8snetworking.api.K8sFlowRuleService;
Jian Li4aa17642019-01-30 00:01:11 +090027import org.onosproject.k8snetworking.api.K8sNetwork;
28import org.onosproject.k8snetworking.api.K8sNetworkEvent;
29import org.onosproject.k8snetworking.api.K8sNetworkListener;
30import org.onosproject.k8snetworking.api.K8sNetworkService;
Jian Li66f6e3c2019-01-25 10:24:44 +090031import org.onosproject.k8snode.api.K8sNode;
32import org.onosproject.k8snode.api.K8sNodeEvent;
33import org.onosproject.k8snode.api.K8sNodeListener;
34import org.onosproject.k8snode.api.K8sNodeService;
35import org.onosproject.net.DeviceId;
Jian Li4aa17642019-01-30 00:01:11 +090036import org.onosproject.net.PortNumber;
Jian Li7d111d72019-04-12 13:58:44 +090037import org.onosproject.net.device.DeviceService;
Jian Li66f6e3c2019-01-25 10:24:44 +090038import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleOperations;
43import org.onosproject.net.flow.FlowRuleOperationsContext;
44import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.osgi.service.component.annotations.Activate;
48import org.osgi.service.component.annotations.Component;
49import org.osgi.service.component.annotations.Deactivate;
50import org.osgi.service.component.annotations.Reference;
51import org.osgi.service.component.annotations.ReferenceCardinality;
52import org.slf4j.Logger;
53
54import java.util.Objects;
55import java.util.concurrent.ExecutorService;
56import java.util.concurrent.Executors;
57
58import static org.onlab.util.Tools.groupedThreads;
Jian Li73d3b6a2019-07-08 18:07:53 +090059import static org.onosproject.k8snetworking.api.Constants.ACL_TABLE;
Jian Li66f6e3c2019-01-25 10:24:44 +090060import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
61import static org.onosproject.k8snetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Jian Li66f6e3c2019-01-25 10:24:44 +090062import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
Jian Li73d3b6a2019-07-08 18:07:53 +090063import static org.onosproject.k8snetworking.api.Constants.GROUPING_TABLE;
Jian Lie1a5b8f2019-07-23 17:13:19 +090064import static org.onosproject.k8snetworking.api.Constants.JUMP_TABLE;
Jian Li66f6e3c2019-01-25 10:24:44 +090065import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Lie1a5b8f2019-07-23 17:13:19 +090066import static org.onosproject.k8snetworking.api.Constants.NAMESPACE_TABLE;
Jian Li7d111d72019-04-12 13:58:44 +090067import static org.onosproject.k8snetworking.api.Constants.PRIORITY_CIDR_RULE;
Jian Li66f6e3c2019-01-25 10:24:44 +090068import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SNAT_RULE;
69import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
Jian Li73d3b6a2019-07-08 18:07:53 +090070import static org.onosproject.k8snetworking.api.Constants.STAT_EGRESS_TABLE;
Jian Lie1a5b8f2019-07-23 17:13:19 +090071import static org.onosproject.k8snetworking.api.Constants.STAT_INGRESS_TABLE;
Jian Li66f6e3c2019-01-25 10:24:44 +090072import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
Jian Li73d3b6a2019-07-08 18:07:53 +090073import static org.onosproject.k8snetworking.api.Constants.VTAP_EGRESS_TABLE;
Jian Lie1a5b8f2019-07-23 17:13:19 +090074import static org.onosproject.k8snetworking.api.Constants.VTAP_INGRESS_TABLE;
Jian Li7d111d72019-04-12 13:58:44 +090075import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
76import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
Jian Li66f6e3c2019-01-25 10:24:44 +090077import static org.slf4j.LoggerFactory.getLogger;
78
79/**
80 * Sets flow rules directly using FlowRuleService.
81 */
82@Component(immediate = true, service = K8sFlowRuleService.class)
83public class K8sFlowRuleManager implements K8sFlowRuleService {
84
85 private final Logger log = getLogger(getClass());
86
87 private static final int DROP_PRIORITY = 0;
88 private static final int HIGH_PRIORITY = 30000;
89 private static final int TIMEOUT_SNAT_RULE = 60;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected FlowRuleService flowRuleService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected CoreService coreService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li7d111d72019-04-12 13:58:44 +090098 protected DeviceService deviceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li66f6e3c2019-01-25 10:24:44 +0900101 protected ClusterService clusterService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected LeadershipService leadershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +0900107 protected K8sNetworkService k8sNetworkService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li66f6e3c2019-01-25 10:24:44 +0900110 protected K8sNodeService k8sNodeService;
111
112 private final ExecutorService deviceEventExecutor =
113 Executors.newSingleThreadExecutor(groupedThreads(
114 getClass().getSimpleName(), "device-event"));
Jian Li4aa17642019-01-30 00:01:11 +0900115 private final K8sNetworkListener internalNetworkListener = new InternalK8sNetworkListener();
Jian Li66f6e3c2019-01-25 10:24:44 +0900116 private final K8sNodeListener internalNodeListener = new InternalK8sNodeListener();
117
118 private ApplicationId appId;
119 private NodeId localNodeId;
120
121 @Activate
122 protected void activate() {
123 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
124 coreService.registerApplication(K8S_NETWORKING_APP_ID);
125 k8sNodeService.addListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900126 k8sNetworkService.addListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900127 localNodeId = clusterService.getLocalNode().id();
128 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900129 k8sNodeService.completeNodes().forEach(this::initializePipeline);
Jian Li66f6e3c2019-01-25 10:24:44 +0900130
131 log.info("Started");
132 }
133
134 @Deactivate
135 protected void deactivate() {
136 k8sNodeService.removeListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900137 k8sNetworkService.removeListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900138 leadershipService.withdraw(appId.name());
139 deviceEventExecutor.shutdown();
140
141 log.info("Stopped");
142 }
143
144 @Override
145 public void setRule(ApplicationId appId, DeviceId deviceId,
146 TrafficSelector selector, TrafficTreatment treatment,
147 int priority, int tableType, boolean install) {
148 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder()
149 .forDevice(deviceId)
150 .withSelector(selector)
151 .withTreatment(treatment)
152 .withPriority(priority)
153 .fromApp(appId)
154 .forTable(tableType);
155
156 if (priority == PRIORITY_SNAT_RULE) {
157 flowRuleBuilder.makeTemporary(TIMEOUT_SNAT_RULE);
158 } else {
159 flowRuleBuilder.makePermanent();
160 }
161
162 applyRule(flowRuleBuilder.build(), install);
163 }
164
165 @Override
166 public void setUpTableMissEntry(DeviceId deviceId, int table) {
167 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
168 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
169
170 treatment.drop();
171
172 FlowRule flowRule = DefaultFlowRule.builder()
173 .forDevice(deviceId)
174 .withSelector(selector.build())
175 .withTreatment(treatment.build())
176 .withPriority(DROP_PRIORITY)
177 .fromApp(appId)
178 .makePermanent()
179 .forTable(table)
180 .build();
181
182 applyRule(flowRule, true);
183 }
184
185 @Override
186 public void connectTables(DeviceId deviceId, int fromTable, int toTable) {
187 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
188 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
189
190 treatment.transition(toTable);
191
192 FlowRule flowRule = DefaultFlowRule.builder()
193 .forDevice(deviceId)
194 .withSelector(selector.build())
195 .withTreatment(treatment.build())
196 .withPriority(DROP_PRIORITY)
197 .fromApp(appId)
198 .makePermanent()
199 .forTable(fromTable)
200 .build();
201
202 applyRule(flowRule, true);
203 }
204
205 private void applyRule(FlowRule flowRule, boolean install) {
206 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
207
208 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
209
210 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
211 @Override
212 public void onSuccess(FlowRuleOperations ops) {
213 log.debug("Provisioned vni or forwarding table");
214 }
215
216 @Override
217 public void onError(FlowRuleOperations ops) {
218 log.debug("Failed to provision vni or forwarding table");
219 }
220 }));
221 }
222
Jian Li4aa17642019-01-30 00:01:11 +0900223 protected void initializePipeline(K8sNode k8sNode) {
224
225 DeviceId deviceId = k8sNode.intgBridge();
226
Jian Li66f6e3c2019-01-25 10:24:44 +0900227 // for inbound table transition
Jian Li73d3b6a2019-07-08 18:07:53 +0900228 connectTables(deviceId, STAT_INGRESS_TABLE, VTAP_INGRESS_TABLE);
229 connectTables(deviceId, VTAP_INGRESS_TABLE, VTAG_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900230
231 // for vTag and ARP table transition
232 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
233
Jian Lie1a5b8f2019-07-23 17:13:19 +0900234 // for jump and namespace table transition
235 connectTables(deviceId, JUMP_TABLE, NAMESPACE_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900236
Jian Li4aa17642019-01-30 00:01:11 +0900237 // for ARP and ACL table transition
Jian Lie1a5b8f2019-07-23 17:13:19 +0900238 connectTables(deviceId, ARP_TABLE, NAMESPACE_TABLE);
239
240 // for namespace table transition to grouping table
241 connectTables(deviceId, NAMESPACE_TABLE, GROUPING_TABLE);
Jian Li4aa17642019-01-30 00:01:11 +0900242
Jian Li73d3b6a2019-07-08 18:07:53 +0900243 // for grouping table transition to ACL table
244 connectTables(deviceId, GROUPING_TABLE, ACL_TABLE);
Jian Li7d111d72019-04-12 13:58:44 +0900245
Jian Li73d3b6a2019-07-08 18:07:53 +0900246 // for ACL table transition to routing table
247 connectTables(deviceId, ACL_TABLE, ROUTING_TABLE);
248
249 // for grouping table transition
250 // we need grouping table for bypassing routing table which contains large
Jian Li66f6e3c2019-01-25 10:24:44 +0900251 // amount of flow rules which might cause performance degradation during
252 // table lookup
Jian Li2cc2b632019-02-18 00:56:40 +0900253 // setupJumpTable(k8sNode);
254
255 // for routing and outbound table transition
Jian Li73d3b6a2019-07-08 18:07:53 +0900256 connectTables(deviceId, ROUTING_TABLE, STAT_EGRESS_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900257
258 // for outbound table transition
Jian Li73d3b6a2019-07-08 18:07:53 +0900259 connectTables(deviceId, STAT_EGRESS_TABLE, VTAP_EGRESS_TABLE);
260 connectTables(deviceId, VTAP_EGRESS_TABLE, FORWARDING_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900261 }
262
Jian Li4aa17642019-01-30 00:01:11 +0900263 private void setupJumpTable(K8sNode k8sNode) {
264 DeviceId deviceId = k8sNode.intgBridge();
265
Jian Li66f6e3c2019-01-25 10:24:44 +0900266 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
267 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
268
269 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
270 treatment.transition(ROUTING_TABLE);
271
272 FlowRule flowRule = DefaultFlowRule.builder()
273 .forDevice(deviceId)
274 .withSelector(selector.build())
275 .withTreatment(treatment.build())
276 .withPriority(HIGH_PRIORITY)
277 .fromApp(appId)
278 .makePermanent()
Jian Li73d3b6a2019-07-08 18:07:53 +0900279 .forTable(GROUPING_TABLE)
Jian Li66f6e3c2019-01-25 10:24:44 +0900280 .build();
281
282 applyRule(flowRule, true);
283
284 selector = DefaultTrafficSelector.builder();
285 treatment = DefaultTrafficTreatment.builder();
286
Jian Li73d3b6a2019-07-08 18:07:53 +0900287 treatment.transition(STAT_EGRESS_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900288
289 flowRule = DefaultFlowRule.builder()
290 .forDevice(deviceId)
291 .withSelector(selector.build())
292 .withTreatment(treatment.build())
293 .withPriority(DROP_PRIORITY)
294 .fromApp(appId)
295 .makePermanent()
Jian Li73d3b6a2019-07-08 18:07:53 +0900296 .forTable(GROUPING_TABLE)
Jian Li66f6e3c2019-01-25 10:24:44 +0900297 .build();
298
299 applyRule(flowRule, true);
300 }
301
Jian Li7d111d72019-04-12 13:58:44 +0900302 private void setAnyRoutingRule(IpPrefix srcIpPrefix, MacAddress mac,
303 K8sNetwork k8sNetwork) {
Jian Li2cc2b632019-02-18 00:56:40 +0900304 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
305 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li004526d2019-02-25 16:26:27 +0900306 .matchIPSrc(srcIpPrefix)
Jian Li4aa17642019-01-30 00:01:11 +0900307 .matchIPDst(IpPrefix.valueOf(k8sNetwork.cidr()));
308
Jian Li4aa17642019-01-30 00:01:11 +0900309 for (K8sNode node : k8sNodeService.completeNodes()) {
Jian Li7d111d72019-04-12 13:58:44 +0900310 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
311 .setTunnelId(Long.valueOf(k8sNetwork.segmentId()));
312
313 if (node.hostname().equals(k8sNetwork.name())) {
314 if (mac != null) {
315 tBuilder.setEthSrc(mac);
316 }
Jian Li73d3b6a2019-07-08 18:07:53 +0900317 tBuilder.transition(STAT_EGRESS_TABLE);
Jian Li7d111d72019-04-12 13:58:44 +0900318 } else {
319 PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
320 k8sNetworkService, node);
321 K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
322
323 tBuilder.extension(buildExtension(
324 deviceService,
325 node.intgBridge(),
326 localNode.dataIp().getIp4Address()),
327 node.intgBridge())
328 .setOutput(portNum);
329 }
330
Jian Li4aa17642019-01-30 00:01:11 +0900331 FlowRule flowRule = DefaultFlowRule.builder()
332 .forDevice(node.intgBridge())
333 .withSelector(sBuilder.build())
334 .withTreatment(tBuilder.build())
Jian Li7d111d72019-04-12 13:58:44 +0900335 .withPriority(PRIORITY_CIDR_RULE)
Jian Li4aa17642019-01-30 00:01:11 +0900336 .fromApp(appId)
337 .makePermanent()
Jian Li2cc2b632019-02-18 00:56:40 +0900338 .forTable(ROUTING_TABLE)
339 .build();
340 applyRule(flowRule, true);
341 }
342 }
343
Jian Li004526d2019-02-25 16:26:27 +0900344 private void setupHostRoutingRule(K8sNetwork k8sNetwork) {
Jian Li7d111d72019-04-12 13:58:44 +0900345 setAnyRoutingRule(IpPrefix.valueOf(
346 k8sNetwork.gatewayIp(), 32), null, k8sNetwork);
Jian Li4aa17642019-01-30 00:01:11 +0900347 }
348
Jian Li66f6e3c2019-01-25 10:24:44 +0900349 private class InternalK8sNodeListener implements K8sNodeListener {
350 private boolean isRelevantHelper() {
351 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
352 }
353
354 @Override
355 public void event(K8sNodeEvent event) {
Jian Li66f6e3c2019-01-25 10:24:44 +0900356 switch (event.type()) {
357 case K8S_NODE_COMPLETE:
Jian Li1cee9882019-02-13 11:25:25 +0900358 deviceEventExecutor.execute(() -> processNodeCompletion(event.subject()));
Jian Li66f6e3c2019-01-25 10:24:44 +0900359 break;
360 case K8S_NODE_CREATED:
361 default:
362 // do nothing
363 break;
364 }
365 }
Jian Li1cee9882019-02-13 11:25:25 +0900366
367 private void processNodeCompletion(K8sNode node) {
368 log.info("COMPLETE node {} is detected", node.hostname());
369
370 if (!isRelevantHelper()) {
371 return;
372 }
373
374 initializePipeline(node);
Jian Li7d111d72019-04-12 13:58:44 +0900375
Jian Lif5da78a2019-04-15 01:52:23 +0900376 k8sNetworkService.networks().forEach(K8sFlowRuleManager.this::setupHostRoutingRule);
Jian Li1cee9882019-02-13 11:25:25 +0900377 }
Jian Li66f6e3c2019-01-25 10:24:44 +0900378 }
Jian Li4aa17642019-01-30 00:01:11 +0900379
380 private class InternalK8sNetworkListener implements K8sNetworkListener {
381
382 private boolean isRelevantHelper() {
383 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
384 }
385
386 @Override
387 public void event(K8sNetworkEvent event) {
388
389 switch (event.type()) {
390 case K8S_NETWORK_CREATED:
391 deviceEventExecutor.execute(() -> processNetworkCreation(event.subject()));
392 break;
393 case K8S_NETWORK_REMOVED:
394 break;
395 default:
396 break;
397 }
398 }
399
400 private void processNetworkCreation(K8sNetwork network) {
401 if (!isRelevantHelper()) {
402 return;
403 }
404
Jian Li2cc2b632019-02-18 00:56:40 +0900405 setupHostRoutingRule(network);
Jian Li4aa17642019-01-30 00:01:11 +0900406 }
407 }
Jian Li66f6e3c2019-01-25 10:24:44 +0900408}