blob: 678a2264baed3a85fddd4169fb8b62038d088149 [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;
Jian Li004526d2019-02-25 16:26:27 +090065import static org.onosproject.k8snetworking.api.Constants.SERVICE_IP_CIDR;
Jian Li66f6e3c2019-01-25 10:24:44 +090066import static org.onosproject.k8snetworking.api.Constants.STAT_INBOUND_TABLE;
67import static org.onosproject.k8snetworking.api.Constants.STAT_OUTBOUND_TABLE;
68import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
69import static org.onosproject.k8snetworking.api.Constants.VTAP_INBOUND_TABLE;
70import static org.onosproject.k8snetworking.api.Constants.VTAP_OUTBOUND_TABLE;
71import static org.slf4j.LoggerFactory.getLogger;
72
73/**
74 * Sets flow rules directly using FlowRuleService.
75 */
76@Component(immediate = true, service = K8sFlowRuleService.class)
77public class K8sFlowRuleManager implements K8sFlowRuleService {
78
79 private final Logger log = getLogger(getClass());
80
81 private static final int DROP_PRIORITY = 0;
82 private static final int HIGH_PRIORITY = 30000;
83 private static final int TIMEOUT_SNAT_RULE = 60;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY)
86 protected FlowRuleService flowRuleService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY)
89 protected CoreService coreService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY)
92 protected ClusterService clusterService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected LeadershipService leadershipService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +090098 protected K8sNetworkService k8sNetworkService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li66f6e3c2019-01-25 10:24:44 +0900101 protected K8sNodeService k8sNodeService;
102
103 private final ExecutorService deviceEventExecutor =
104 Executors.newSingleThreadExecutor(groupedThreads(
105 getClass().getSimpleName(), "device-event"));
Jian Li4aa17642019-01-30 00:01:11 +0900106 private final K8sNetworkListener internalNetworkListener = new InternalK8sNetworkListener();
Jian Li66f6e3c2019-01-25 10:24:44 +0900107 private final K8sNodeListener internalNodeListener = new InternalK8sNodeListener();
108
109 private ApplicationId appId;
110 private NodeId localNodeId;
111
112 @Activate
113 protected void activate() {
114 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
115 coreService.registerApplication(K8S_NETWORKING_APP_ID);
116 k8sNodeService.addListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900117 k8sNetworkService.addListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900118 localNodeId = clusterService.getLocalNode().id();
119 leadershipService.runForLeadership(appId.name());
Jian Li4aa17642019-01-30 00:01:11 +0900120 k8sNodeService.completeNodes().forEach(this::initializePipeline);
Jian Li66f6e3c2019-01-25 10:24:44 +0900121
122 log.info("Started");
123 }
124
125 @Deactivate
126 protected void deactivate() {
127 k8sNodeService.removeListener(internalNodeListener);
Jian Li4aa17642019-01-30 00:01:11 +0900128 k8sNetworkService.removeListener(internalNetworkListener);
Jian Li66f6e3c2019-01-25 10:24:44 +0900129 leadershipService.withdraw(appId.name());
130 deviceEventExecutor.shutdown();
131
132 log.info("Stopped");
133 }
134
135 @Override
136 public void setRule(ApplicationId appId, DeviceId deviceId,
137 TrafficSelector selector, TrafficTreatment treatment,
138 int priority, int tableType, boolean install) {
139 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder()
140 .forDevice(deviceId)
141 .withSelector(selector)
142 .withTreatment(treatment)
143 .withPriority(priority)
144 .fromApp(appId)
145 .forTable(tableType);
146
147 if (priority == PRIORITY_SNAT_RULE) {
148 flowRuleBuilder.makeTemporary(TIMEOUT_SNAT_RULE);
149 } else {
150 flowRuleBuilder.makePermanent();
151 }
152
153 applyRule(flowRuleBuilder.build(), install);
154 }
155
156 @Override
157 public void setUpTableMissEntry(DeviceId deviceId, int table) {
158 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
159 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
160
161 treatment.drop();
162
163 FlowRule flowRule = DefaultFlowRule.builder()
164 .forDevice(deviceId)
165 .withSelector(selector.build())
166 .withTreatment(treatment.build())
167 .withPriority(DROP_PRIORITY)
168 .fromApp(appId)
169 .makePermanent()
170 .forTable(table)
171 .build();
172
173 applyRule(flowRule, true);
174 }
175
176 @Override
177 public void connectTables(DeviceId deviceId, int fromTable, int toTable) {
178 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
179 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
180
181 treatment.transition(toTable);
182
183 FlowRule flowRule = DefaultFlowRule.builder()
184 .forDevice(deviceId)
185 .withSelector(selector.build())
186 .withTreatment(treatment.build())
187 .withPriority(DROP_PRIORITY)
188 .fromApp(appId)
189 .makePermanent()
190 .forTable(fromTable)
191 .build();
192
193 applyRule(flowRule, true);
194 }
195
196 private void applyRule(FlowRule flowRule, boolean install) {
197 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
198
199 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
200
201 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
202 @Override
203 public void onSuccess(FlowRuleOperations ops) {
204 log.debug("Provisioned vni or forwarding table");
205 }
206
207 @Override
208 public void onError(FlowRuleOperations ops) {
209 log.debug("Failed to provision vni or forwarding table");
210 }
211 }));
212 }
213
Jian Li4aa17642019-01-30 00:01:11 +0900214 protected void initializePipeline(K8sNode k8sNode) {
215
216 DeviceId deviceId = k8sNode.intgBridge();
217
Jian Li66f6e3c2019-01-25 10:24:44 +0900218 // for inbound table transition
219 connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
Jian Lid89db462019-02-08 18:21:57 +0900220 connectTables(deviceId, VTAP_INBOUND_TABLE, VTAG_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900221
222 // for vTag and ARP table transition
223 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
224
Jian Li66f6e3c2019-01-25 10:24:44 +0900225 connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
226
Jian Li4aa17642019-01-30 00:01:11 +0900227 // for ARP and ACL table transition
228 connectTables(deviceId, ARP_TABLE, JUMP_TABLE);
229
Jian Li66f6e3c2019-01-25 10:24:44 +0900230 // for JUMP table transition
231 // we need JUMP table for bypassing routing table which contains large
232 // amount of flow rules which might cause performance degradation during
233 // table lookup
Jian Li2cc2b632019-02-18 00:56:40 +0900234 // setupJumpTable(k8sNode);
235
236 // for routing and outbound table transition
237 connectTables(deviceId, ROUTING_TABLE, STAT_OUTBOUND_TABLE);
Jian Li66f6e3c2019-01-25 10:24:44 +0900238
239 // for outbound table transition
240 connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
241 connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
242 }
243
Jian Li4aa17642019-01-30 00:01:11 +0900244 private void setupJumpTable(K8sNode k8sNode) {
245 DeviceId deviceId = k8sNode.intgBridge();
246
Jian Li66f6e3c2019-01-25 10:24:44 +0900247 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
248 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
249
250 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
251 treatment.transition(ROUTING_TABLE);
252
253 FlowRule flowRule = DefaultFlowRule.builder()
254 .forDevice(deviceId)
255 .withSelector(selector.build())
256 .withTreatment(treatment.build())
257 .withPriority(HIGH_PRIORITY)
258 .fromApp(appId)
259 .makePermanent()
260 .forTable(JUMP_TABLE)
261 .build();
262
263 applyRule(flowRule, true);
264
265 selector = DefaultTrafficSelector.builder();
266 treatment = DefaultTrafficTreatment.builder();
267
268 treatment.transition(STAT_OUTBOUND_TABLE);
269
270 flowRule = DefaultFlowRule.builder()
271 .forDevice(deviceId)
272 .withSelector(selector.build())
273 .withTreatment(treatment.build())
274 .withPriority(DROP_PRIORITY)
275 .fromApp(appId)
276 .makePermanent()
277 .forTable(JUMP_TABLE)
278 .build();
279
280 applyRule(flowRule, true);
281 }
282
Jian Li004526d2019-02-25 16:26:27 +0900283 private void setAnyRoutingRule(IpPrefix srcIpPrefix, K8sNetwork k8sNetwork) {
Jian Li2cc2b632019-02-18 00:56:40 +0900284 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
285 .matchEthType(Ethernet.TYPE_IPV4)
Jian Li004526d2019-02-25 16:26:27 +0900286 .matchIPSrc(srcIpPrefix)
Jian Li4aa17642019-01-30 00:01:11 +0900287 .matchIPDst(IpPrefix.valueOf(k8sNetwork.cidr()));
288
Jian Li2cc2b632019-02-18 00:56:40 +0900289 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
290 .setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
Jian Li4aa17642019-01-30 00:01:11 +0900291 .transition(STAT_OUTBOUND_TABLE);
292
293 for (K8sNode node : k8sNodeService.completeNodes()) {
294 FlowRule flowRule = DefaultFlowRule.builder()
295 .forDevice(node.intgBridge())
296 .withSelector(sBuilder.build())
297 .withTreatment(tBuilder.build())
298 .withPriority(HIGH_PRIORITY)
299 .fromApp(appId)
300 .makePermanent()
Jian Li2cc2b632019-02-18 00:56:40 +0900301 .forTable(ROUTING_TABLE)
302 .build();
303 applyRule(flowRule, true);
304 }
305 }
306
Jian Li004526d2019-02-25 16:26:27 +0900307 private void setupServiceRoutingRule(K8sNetwork k8sNetwork) {
308 setAnyRoutingRule(IpPrefix.valueOf(SERVICE_IP_CIDR), k8sNetwork);
309 }
310
311 private void setupHostRoutingRule(K8sNetwork k8sNetwork) {
312 setAnyRoutingRule(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32), k8sNetwork);
313 }
314
Jian Li2cc2b632019-02-18 00:56:40 +0900315 private void setupGatewayRoutingRule(K8sNetwork k8sNetwork) {
316 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
317 .matchEthType(Ethernet.TYPE_IPV4)
318 .matchIPDst(IpPrefix.valueOf(k8sNetwork.gatewayIp(), 32));
319
320 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
321 .setOutput(PortNumber.LOCAL);
322
323 for (K8sNode node : k8sNodeService.completeNodes()) {
324 FlowRule flowRule = DefaultFlowRule.builder()
325 .forDevice(node.intgBridge())
326 .withSelector(sBuilder.build())
327 .withTreatment(tBuilder.build())
328 .withPriority(HIGH_PRIORITY)
329 .fromApp(appId)
330 .makePermanent()
331 .forTable(ROUTING_TABLE)
Jian Li4aa17642019-01-30 00:01:11 +0900332 .build();
333 applyRule(flowRule, true);
334 }
335 }
336
Jian Li66f6e3c2019-01-25 10:24:44 +0900337 private class InternalK8sNodeListener implements K8sNodeListener {
338 private boolean isRelevantHelper() {
339 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
340 }
341
342 @Override
343 public void event(K8sNodeEvent event) {
Jian Li66f6e3c2019-01-25 10:24:44 +0900344 switch (event.type()) {
345 case K8S_NODE_COMPLETE:
Jian Li1cee9882019-02-13 11:25:25 +0900346 deviceEventExecutor.execute(() -> processNodeCompletion(event.subject()));
Jian Li66f6e3c2019-01-25 10:24:44 +0900347 break;
348 case K8S_NODE_CREATED:
349 default:
350 // do nothing
351 break;
352 }
353 }
Jian Li1cee9882019-02-13 11:25:25 +0900354
355 private void processNodeCompletion(K8sNode node) {
356 log.info("COMPLETE node {} is detected", node.hostname());
357
358 if (!isRelevantHelper()) {
359 return;
360 }
361
362 initializePipeline(node);
Jian Li2cc2b632019-02-18 00:56:40 +0900363 k8sNetworkService.networks().forEach(n -> {
364 setupHostRoutingRule(n);
Jian Li004526d2019-02-25 16:26:27 +0900365 setupServiceRoutingRule(n);
Jian Li2cc2b632019-02-18 00:56:40 +0900366 setupGatewayRoutingRule(n);
367 });
Jian Li1cee9882019-02-13 11:25:25 +0900368 }
Jian Li66f6e3c2019-01-25 10:24:44 +0900369 }
Jian Li4aa17642019-01-30 00:01:11 +0900370
371 private class InternalK8sNetworkListener implements K8sNetworkListener {
372
373 private boolean isRelevantHelper() {
374 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
375 }
376
377 @Override
378 public void event(K8sNetworkEvent event) {
379
380 switch (event.type()) {
381 case K8S_NETWORK_CREATED:
382 deviceEventExecutor.execute(() -> processNetworkCreation(event.subject()));
383 break;
384 case K8S_NETWORK_REMOVED:
385 break;
386 default:
387 break;
388 }
389 }
390
391 private void processNetworkCreation(K8sNetwork network) {
392 if (!isRelevantHelper()) {
393 return;
394 }
395
Jian Li2cc2b632019-02-18 00:56:40 +0900396 setupHostRoutingRule(network);
397 setupGatewayRoutingRule(network);
Jian Li004526d2019-02-25 16:26:27 +0900398 setupServiceRoutingRule(network);
Jian Li4aa17642019-01-30 00:01:11 +0900399 }
400 }
Jian Li66f6e3c2019-01-25 10:24:44 +0900401}