blob: ffb73e2951bd8433ace0e12e8a0ffd1b4c0dde0e [file] [log] [blame]
Jian Li9871cd52021-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 Park4063f402021-02-25 09:14:22 +090078import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
Jian Li9871cd52021-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)
142 .forEach(node -> initializePipeline(node.intgBridge()));
143
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
248 protected void initializePipeline(DeviceId deviceId) {
249 // for inbound table transition
250 connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
251 connectTables(deviceId, VTAP_INBOUND_TABLE, DHCP_TABLE);
252
253 // for DHCP and vTag table transition
254 connectTables(deviceId, DHCP_TABLE, VTAG_TABLE);
255
256 if (getProviderNetworkOnlyFlag()) {
257 // we directly transit from vTag table to PRE_FLAT table for provider
258 // network only mode, because there is no need to differentiate ARP
259 // and IP packets on this mode
260 connectTables(deviceId, VTAG_TABLE, PRE_FLAT_TABLE);
261 } else {
262 // for vTag and ARP table transition
263 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
264 }
265
266 // for PRE_FLAT and FLAT table transition
267 connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
268
269 // for ARP and ACL table transition
270 connectTables(deviceId, ARP_TABLE, ACL_INGRESS_TABLE);
271
272 // for ACL and JUMP table transition
273 connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
274
275 // for outbound table transition
276 connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
277 connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
278
279 // for JUMP table transition
280 // we need JUMP table for bypassing routing table which contains large
281 // amount of flow rules which might cause performance degradation during
282 // table lookup
283 setupJumpTable(deviceId);
284
285 // for setting up default FLAT table behavior which is NORMAL
286 setupFlatTable(deviceId);
287 }
288
289 private void setupJumpTable(DeviceId deviceId) {
290 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
291 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
292
293 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
294 treatment.transition(ROUTING_TABLE);
295
296 FlowRule flowRule = DefaultFlowRule.builder()
297 .forDevice(deviceId)
298 .withSelector(selector.build())
299 .withTreatment(treatment.build())
300 .withPriority(HIGH_PRIORITY)
301 .fromApp(appId)
302 .makePermanent()
303 .forTable(JUMP_TABLE)
304 .build();
305
306 applyRule(flowRule, true);
307
308 selector = DefaultTrafficSelector.builder();
309 treatment = DefaultTrafficTreatment.builder();
310
311 treatment.transition(STAT_OUTBOUND_TABLE);
312
313 flowRule = DefaultFlowRule.builder()
314 .forDevice(deviceId)
315 .withSelector(selector.build())
316 .withTreatment(treatment.build())
317 .withPriority(DROP_PRIORITY)
318 .fromApp(appId)
319 .makePermanent()
320 .forTable(JUMP_TABLE)
321 .build();
322
323 applyRule(flowRule, true);
324 }
325
326 private void setupFlatTable(DeviceId deviceId) {
327 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
328 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
329 .setOutput(PortNumber.NORMAL);
330
331 FlowRule flowRule = DefaultFlowRule.builder()
332 .forDevice(deviceId)
333 .withSelector(selector.build())
334 .withTreatment(treatment.build())
335 .withPriority(LOW_PRIORITY)
336 .fromApp(appId)
337 .makePermanent()
338 .forTable(FLAT_TABLE)
339 .build();
340
341 applyRule(flowRule, true);
342 }
343
344 private boolean getProviderNetworkOnlyFlag() {
345 Set<ConfigProperty> properties =
346 configService.getProperties(getClass().getName());
347 return getPropertyValueAsBoolean(properties, PROVIDER_NETWORK_ONLY);
348 }
349
350 private class InternalKubevirtNodeListener implements KubevirtNodeListener {
351
352 @Override
353 public boolean isRelevant(KubevirtNodeEvent event) {
Daniel Park4063f402021-02-25 09:14:22 +0900354 return event.subject().type().equals(WORKER) ||
355 event.subject().type().equals(GATEWAY);
Jian Li9871cd52021-01-09 00:19:02 +0900356 }
357
358 private boolean isRelevantHelper() {
359 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
360 }
361
362 @Override
363 public void event(KubevirtNodeEvent event) {
364 KubevirtNode node = event.subject();
365
366 switch (event.type()) {
367 case KUBEVIRT_NODE_COMPLETE:
368 deviceEventExecutor.execute(() -> {
369 log.info("COMPLETE node {} is detected", node.hostname());
370
371 if (!isRelevantHelper()) {
372 return;
373 }
374
375 initializePipeline(node.intgBridge());
376 });
377 break;
378 case KUBEVIRT_NODE_CREATED:
379 case KUBEVIRT_NODE_UPDATED:
380 case KUBEVIRT_NODE_REMOVED:
381 default:
382 // do nothing
383 break;
384 }
385 }
386 }
387}