blob: 585c925e70227173ac448fb09631a7acbfae5887 [file] [log] [blame]
Jian Li43244382021-01-09 00:19:02 +09001/*
2 * Copyright 2021-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.kubevirtnetworking.impl;
17
18import org.onlab.util.Tools;
19import org.onosproject.cfg.ComponentConfigService;
20import org.onosproject.cfg.ConfigProperty;
21import 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.kubevirtnetworking.api.KubevirtFlowRuleService;
27import org.onosproject.kubevirtnode.api.KubevirtNode;
28import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
29import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
30import org.onosproject.kubevirtnode.api.KubevirtNodeService;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.flow.DefaultFlowRule;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.FlowRule;
38import org.onosproject.net.flow.FlowRuleOperations;
39import org.onosproject.net.flow.FlowRuleOperationsContext;
40import org.onosproject.net.flow.FlowRuleService;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.osgi.service.component.ComponentContext;
44import org.osgi.service.component.annotations.Activate;
45import org.osgi.service.component.annotations.Component;
46import org.osgi.service.component.annotations.Deactivate;
47import org.osgi.service.component.annotations.Modified;
48import org.osgi.service.component.annotations.Reference;
49import org.osgi.service.component.annotations.ReferenceCardinality;
50import org.slf4j.Logger;
51
52import java.util.Dictionary;
53import java.util.Objects;
54import java.util.Set;
55import java.util.concurrent.ExecutorService;
56import java.util.concurrent.Executors;
57
58import static org.onlab.util.Tools.groupedThreads;
59import static org.onosproject.kubevirtnetworking.api.Constants.ACL_EGRESS_TABLE;
60import static org.onosproject.kubevirtnetworking.api.Constants.ACL_INGRESS_TABLE;
61import static org.onosproject.kubevirtnetworking.api.Constants.ARP_TABLE;
62import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
63import static org.onosproject.kubevirtnetworking.api.Constants.DHCP_TABLE;
64import static org.onosproject.kubevirtnetworking.api.Constants.FLAT_TABLE;
65import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
66import static org.onosproject.kubevirtnetworking.api.Constants.JUMP_TABLE;
67import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
68import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
69import static org.onosproject.kubevirtnetworking.api.Constants.ROUTING_TABLE;
70import static org.onosproject.kubevirtnetworking.api.Constants.STAT_INBOUND_TABLE;
71import static org.onosproject.kubevirtnetworking.api.Constants.STAT_OUTBOUND_TABLE;
72import static org.onosproject.kubevirtnetworking.api.Constants.VTAG_TABLE;
73import static org.onosproject.kubevirtnetworking.api.Constants.VTAP_INBOUND_TABLE;
74import static org.onosproject.kubevirtnetworking.api.Constants.VTAP_OUTBOUND_TABLE;
75import static org.onosproject.kubevirtnetworking.impl.OsgiPropertyConstants.PROVIDER_NETWORK_ONLY;
76import static org.onosproject.kubevirtnetworking.impl.OsgiPropertyConstants.PROVIDER_NETWORK_ONLY_DEFAULT;
77import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getPropertyValueAsBoolean;
Daniel Parka8968802021-02-25 09:14:22 +090078import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
Jian Li43244382021-01-09 00:19:02 +090079import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
80import static org.slf4j.LoggerFactory.getLogger;
81
82/**
83 * Sets flow rules directly using FlowRuleService.
84 */
85@Component(
86 immediate = true,
87 service = KubevirtFlowRuleService.class,
88 property = {
89 PROVIDER_NETWORK_ONLY + ":Boolean=" + PROVIDER_NETWORK_ONLY_DEFAULT
90 }
91)
92public class KubevirtFlowRuleManager implements KubevirtFlowRuleService {
93
94 private final Logger log = getLogger(getClass());
95
96 private static final int DROP_PRIORITY = 0;
97 private static final int LOW_PRIORITY = 10000;
98 private static final int MID_PRIORITY = 20000;
99 private static final int HIGH_PRIORITY = 30000;
100 private static final int TIMEOUT_SNAT_RULE = 60;
101
102 /** Use provider network only. */
103 private boolean providerNetworkOnly = PROVIDER_NETWORK_ONLY_DEFAULT;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected FlowRuleService flowRuleService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected CoreService coreService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected ClusterService clusterService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected LeadershipService leadershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
118 protected DeviceService deviceService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
121 protected ComponentConfigService configService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
124 protected KubevirtNodeService nodeService;
125
126 private final ExecutorService deviceEventExecutor = Executors.newSingleThreadExecutor(
127 groupedThreads(getClass().getSimpleName(), "device-event"));
128 private final KubevirtNodeListener internalNodeListener = new InternalKubevirtNodeListener();
129
130 private ApplicationId appId;
131 private NodeId localNodeId;
132
133 @Activate
134 protected void activate() {
135 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
136 coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
137 configService.registerProperties(getClass());
138 nodeService.addListener(internalNodeListener);
139 localNodeId = clusterService.getLocalNode().id();
140 leadershipService.runForLeadership(appId.name());
141 nodeService.completeNodes(WORKER)
Daniel Park2884b232021-03-04 18:58:47 +0900142 .forEach(node -> initializeWorkerNodePipeline(node.intgBridge()));
Jian Li43244382021-01-09 00:19:02 +0900143
144 log.info("Started");
145 }
146
147 @Deactivate
148 protected void deactivate() {
149 nodeService.removeListener(internalNodeListener);
150 configService.unregisterProperties(getClass(), false);
151 leadershipService.withdraw(appId.name());
152 deviceEventExecutor.shutdown();
153
154 log.info("Stopped");
155 }
156
157 @Modified
158 protected void modified(ComponentContext context) {
159 Dictionary<?, ?> properties = context.getProperties();
160 Boolean flag;
161
162 flag = Tools.isPropertyEnabled(properties, PROVIDER_NETWORK_ONLY);
163 if (flag == null) {
164 log.info("providerNetworkOnly is not configured, " +
165 "using current value of {}", providerNetworkOnly);
166 } else {
167 providerNetworkOnly = flag;
168 log.info("Configured. providerNetworkOnly is {}",
169 providerNetworkOnly ? "enabled" : "disabled");
170 }
171 }
172
173 @Override
174 public void setRule(ApplicationId appId, DeviceId deviceId,
175 TrafficSelector selector, TrafficTreatment treatment,
176 int priority, int tableType, boolean install) {
177
178 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder()
179 .forDevice(deviceId)
180 .withSelector(selector)
181 .withTreatment(treatment)
182 .withPriority(priority)
183 .fromApp(appId)
184 .forTable(tableType)
185 .makePermanent();
186
187 applyRule(flowRuleBuilder.build(), install);
188 }
189
190 @Override
191 public void setUpTableMissEntry(DeviceId deviceId, int table) {
192 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
193 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
194
195 treatment.drop();
196
197 FlowRule flowRule = DefaultFlowRule.builder()
198 .forDevice(deviceId)
199 .withSelector(selector.build())
200 .withTreatment(treatment.build())
201 .withPriority(DROP_PRIORITY)
202 .fromApp(appId)
203 .makePermanent()
204 .forTable(table)
205 .build();
206
207 applyRule(flowRule, true);
208 }
209
210 @Override
211 public void connectTables(DeviceId deviceId, int fromTable, int toTable) {
212 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
213 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
214
215 treatment.transition(toTable);
216
217 FlowRule flowRule = DefaultFlowRule.builder()
218 .forDevice(deviceId)
219 .withSelector(selector.build())
220 .withTreatment(treatment.build())
221 .withPriority(DROP_PRIORITY)
222 .fromApp(appId)
223 .makePermanent()
224 .forTable(fromTable)
225 .build();
226
227 applyRule(flowRule, true);
228 }
229
230 private void applyRule(FlowRule flowRule, boolean install) {
231 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
232
233 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
234
235 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
236 @Override
237 public void onSuccess(FlowRuleOperations ops) {
238 log.debug("Provisioned vni or forwarding table");
239 }
240
241 @Override
242 public void onError(FlowRuleOperations ops) {
243 log.debug("Failed to provision vni or forwarding table");
244 }
245 }));
246 }
247
Daniel Park2884b232021-03-04 18:58:47 +0900248 protected void initializeGatewayNodePipeline(DeviceId deviceId) {
249 // for inbound table transition
250 connectTables(deviceId, STAT_INBOUND_TABLE, VTAG_TABLE);
251
252 if (getProviderNetworkOnlyFlag()) {
253 // we directly transit from vTag table to PRE_FLAT table for provider
254 // network only mode, because there is no need to differentiate ARP
255 // and IP packets on this mode
256 connectTables(deviceId, VTAG_TABLE, PRE_FLAT_TABLE);
257 } else {
258 // for vTag and ARP table transition
259 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
260 }
261
262 // for PRE_FLAT and FLAT table transition
263 connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
264
265 // for setting up default FLAT table behavior which is drop
266 setupGatewayNodeFlatTable(deviceId);
267
268 // for setting up default Forwarding table behavior which is NORMAL
269 setupForwardingTable(deviceId);
270 }
271 protected void initializeWorkerNodePipeline(DeviceId deviceId) {
Jian Li43244382021-01-09 00:19:02 +0900272 // for inbound table transition
273 connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
274 connectTables(deviceId, VTAP_INBOUND_TABLE, DHCP_TABLE);
275
276 // for DHCP and vTag table transition
277 connectTables(deviceId, DHCP_TABLE, VTAG_TABLE);
278
279 if (getProviderNetworkOnlyFlag()) {
280 // we directly transit from vTag table to PRE_FLAT table for provider
281 // network only mode, because there is no need to differentiate ARP
282 // and IP packets on this mode
283 connectTables(deviceId, VTAG_TABLE, PRE_FLAT_TABLE);
284 } else {
285 // for vTag and ARP table transition
286 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
287 }
288
289 // for PRE_FLAT and FLAT table transition
290 connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
291
Daniel Park2884b232021-03-04 18:58:47 +0900292 // for FLAT table and ACL table transition
293 connectTables(deviceId, FLAT_TABLE, ACL_EGRESS_TABLE);
294
Jian Li43244382021-01-09 00:19:02 +0900295 // for ARP and ACL table transition
296 connectTables(deviceId, ARP_TABLE, ACL_INGRESS_TABLE);
297
298 // for ACL and JUMP table transition
299 connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
300
301 // for outbound table transition
302 connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
303 connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
304
305 // for JUMP table transition
306 // we need JUMP table for bypassing routing table which contains large
307 // amount of flow rules which might cause performance degradation during
308 // table lookup
309 setupJumpTable(deviceId);
310
Daniel Park2884b232021-03-04 18:58:47 +0900311 // for setting up default Forwarding table behavior which is NORMAL
312 setupForwardingTable(deviceId);
Jian Li43244382021-01-09 00:19:02 +0900313 }
314
315 private void setupJumpTable(DeviceId deviceId) {
316 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
317 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
318
319 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
320 treatment.transition(ROUTING_TABLE);
321
322 FlowRule flowRule = DefaultFlowRule.builder()
323 .forDevice(deviceId)
324 .withSelector(selector.build())
325 .withTreatment(treatment.build())
326 .withPriority(HIGH_PRIORITY)
327 .fromApp(appId)
328 .makePermanent()
329 .forTable(JUMP_TABLE)
330 .build();
331
332 applyRule(flowRule, true);
333
334 selector = DefaultTrafficSelector.builder();
335 treatment = DefaultTrafficTreatment.builder();
336
337 treatment.transition(STAT_OUTBOUND_TABLE);
338
339 flowRule = DefaultFlowRule.builder()
340 .forDevice(deviceId)
341 .withSelector(selector.build())
342 .withTreatment(treatment.build())
343 .withPriority(DROP_PRIORITY)
344 .fromApp(appId)
345 .makePermanent()
346 .forTable(JUMP_TABLE)
347 .build();
348
349 applyRule(flowRule, true);
350 }
351
Daniel Park2884b232021-03-04 18:58:47 +0900352 private void setupForwardingTable(DeviceId deviceId) {
Jian Li43244382021-01-09 00:19:02 +0900353 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
354 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
355 .setOutput(PortNumber.NORMAL);
356
357 FlowRule flowRule = DefaultFlowRule.builder()
358 .forDevice(deviceId)
359 .withSelector(selector.build())
360 .withTreatment(treatment.build())
361 .withPriority(LOW_PRIORITY)
362 .fromApp(appId)
363 .makePermanent()
Daniel Park2884b232021-03-04 18:58:47 +0900364 .forTable(FORWARDING_TABLE)
365 .build();
366
367 applyRule(flowRule, true);
368 }
369
370 private void setupGatewayNodeFlatTable(DeviceId deviceId) {
371 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
372 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
373 .drop();
374
375 FlowRule flowRule = DefaultFlowRule.builder()
376 .forDevice(deviceId)
377 .withSelector(selector.build())
378 .withTreatment(treatment.build())
379 .withPriority(DROP_PRIORITY)
380 .fromApp(appId)
381 .makePermanent()
Jian Li43244382021-01-09 00:19:02 +0900382 .forTable(FLAT_TABLE)
383 .build();
384
385 applyRule(flowRule, true);
Daniel Park2884b232021-03-04 18:58:47 +0900386
Jian Li43244382021-01-09 00:19:02 +0900387 }
388
389 private boolean getProviderNetworkOnlyFlag() {
390 Set<ConfigProperty> properties =
391 configService.getProperties(getClass().getName());
392 return getPropertyValueAsBoolean(properties, PROVIDER_NETWORK_ONLY);
393 }
394
395 private class InternalKubevirtNodeListener implements KubevirtNodeListener {
396
397 @Override
398 public boolean isRelevant(KubevirtNodeEvent event) {
Daniel Parka8968802021-02-25 09:14:22 +0900399 return event.subject().type().equals(WORKER) ||
400 event.subject().type().equals(GATEWAY);
Jian Li43244382021-01-09 00:19:02 +0900401 }
402
403 private boolean isRelevantHelper() {
404 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
405 }
406
407 @Override
408 public void event(KubevirtNodeEvent event) {
409 KubevirtNode node = event.subject();
410
411 switch (event.type()) {
412 case KUBEVIRT_NODE_COMPLETE:
413 deviceEventExecutor.execute(() -> {
414 log.info("COMPLETE node {} is detected", node.hostname());
415
416 if (!isRelevantHelper()) {
417 return;
418 }
419
Daniel Park2884b232021-03-04 18:58:47 +0900420 if (event.subject().type().equals(WORKER)) {
421 initializeWorkerNodePipeline(node.intgBridge());
422 } else {
423 initializeGatewayNodePipeline(node.intgBridge());
424 }
Jian Li43244382021-01-09 00:19:02 +0900425 });
426 break;
427 case KUBEVIRT_NODE_CREATED:
428 case KUBEVIRT_NODE_UPDATED:
429 case KUBEVIRT_NODE_REMOVED:
430 default:
431 // do nothing
432 break;
433 }
434 }
435 }
436}