blob: fa5aeda69e41e58e926c0909e6a08dedbc0689e2 [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;
78import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
79import static org.slf4j.LoggerFactory.getLogger;
80
81/**
82 * Sets flow rules directly using FlowRuleService.
83 */
84@Component(
85 immediate = true,
86 service = KubevirtFlowRuleService.class,
87 property = {
88 PROVIDER_NETWORK_ONLY + ":Boolean=" + PROVIDER_NETWORK_ONLY_DEFAULT
89 }
90)
91public class KubevirtFlowRuleManager implements KubevirtFlowRuleService {
92
93 private final Logger log = getLogger(getClass());
94
95 private static final int DROP_PRIORITY = 0;
96 private static final int LOW_PRIORITY = 10000;
97 private static final int MID_PRIORITY = 20000;
98 private static final int HIGH_PRIORITY = 30000;
99 private static final int TIMEOUT_SNAT_RULE = 60;
100
101 /** Use provider network only. */
102 private boolean providerNetworkOnly = PROVIDER_NETWORK_ONLY_DEFAULT;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected FlowRuleService flowRuleService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
108 protected CoreService coreService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
111 protected ClusterService clusterService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
114 protected LeadershipService leadershipService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
117 protected DeviceService deviceService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
120 protected ComponentConfigService configService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
123 protected KubevirtNodeService nodeService;
124
125 private final ExecutorService deviceEventExecutor = Executors.newSingleThreadExecutor(
126 groupedThreads(getClass().getSimpleName(), "device-event"));
127 private final KubevirtNodeListener internalNodeListener = new InternalKubevirtNodeListener();
128
129 private ApplicationId appId;
130 private NodeId localNodeId;
131
132 @Activate
133 protected void activate() {
134 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
135 coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
136 configService.registerProperties(getClass());
137 nodeService.addListener(internalNodeListener);
138 localNodeId = clusterService.getLocalNode().id();
139 leadershipService.runForLeadership(appId.name());
140 nodeService.completeNodes(WORKER)
141 .forEach(node -> initializePipeline(node.intgBridge()));
142
143 log.info("Started");
144 }
145
146 @Deactivate
147 protected void deactivate() {
148 nodeService.removeListener(internalNodeListener);
149 configService.unregisterProperties(getClass(), false);
150 leadershipService.withdraw(appId.name());
151 deviceEventExecutor.shutdown();
152
153 log.info("Stopped");
154 }
155
156 @Modified
157 protected void modified(ComponentContext context) {
158 Dictionary<?, ?> properties = context.getProperties();
159 Boolean flag;
160
161 flag = Tools.isPropertyEnabled(properties, PROVIDER_NETWORK_ONLY);
162 if (flag == null) {
163 log.info("providerNetworkOnly is not configured, " +
164 "using current value of {}", providerNetworkOnly);
165 } else {
166 providerNetworkOnly = flag;
167 log.info("Configured. providerNetworkOnly is {}",
168 providerNetworkOnly ? "enabled" : "disabled");
169 }
170 }
171
172 @Override
173 public void setRule(ApplicationId appId, DeviceId deviceId,
174 TrafficSelector selector, TrafficTreatment treatment,
175 int priority, int tableType, boolean install) {
176
177 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder()
178 .forDevice(deviceId)
179 .withSelector(selector)
180 .withTreatment(treatment)
181 .withPriority(priority)
182 .fromApp(appId)
183 .forTable(tableType)
184 .makePermanent();
185
186 applyRule(flowRuleBuilder.build(), install);
187 }
188
189 @Override
190 public void setUpTableMissEntry(DeviceId deviceId, int table) {
191 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
192 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
193
194 treatment.drop();
195
196 FlowRule flowRule = DefaultFlowRule.builder()
197 .forDevice(deviceId)
198 .withSelector(selector.build())
199 .withTreatment(treatment.build())
200 .withPriority(DROP_PRIORITY)
201 .fromApp(appId)
202 .makePermanent()
203 .forTable(table)
204 .build();
205
206 applyRule(flowRule, true);
207 }
208
209 @Override
210 public void connectTables(DeviceId deviceId, int fromTable, int toTable) {
211 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
212 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
213
214 treatment.transition(toTable);
215
216 FlowRule flowRule = DefaultFlowRule.builder()
217 .forDevice(deviceId)
218 .withSelector(selector.build())
219 .withTreatment(treatment.build())
220 .withPriority(DROP_PRIORITY)
221 .fromApp(appId)
222 .makePermanent()
223 .forTable(fromTable)
224 .build();
225
226 applyRule(flowRule, true);
227 }
228
229 private void applyRule(FlowRule flowRule, boolean install) {
230 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
231
232 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
233
234 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
235 @Override
236 public void onSuccess(FlowRuleOperations ops) {
237 log.debug("Provisioned vni or forwarding table");
238 }
239
240 @Override
241 public void onError(FlowRuleOperations ops) {
242 log.debug("Failed to provision vni or forwarding table");
243 }
244 }));
245 }
246
247 protected void initializePipeline(DeviceId deviceId) {
248 // for inbound table transition
249 connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
250 connectTables(deviceId, VTAP_INBOUND_TABLE, DHCP_TABLE);
251
252 // for DHCP and vTag table transition
253 connectTables(deviceId, DHCP_TABLE, VTAG_TABLE);
254
255 if (getProviderNetworkOnlyFlag()) {
256 // we directly transit from vTag table to PRE_FLAT table for provider
257 // network only mode, because there is no need to differentiate ARP
258 // and IP packets on this mode
259 connectTables(deviceId, VTAG_TABLE, PRE_FLAT_TABLE);
260 } else {
261 // for vTag and ARP table transition
262 connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
263 }
264
265 // for PRE_FLAT and FLAT table transition
266 connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
267
268 // for ARP and ACL table transition
269 connectTables(deviceId, ARP_TABLE, ACL_INGRESS_TABLE);
270
271 // for ACL and JUMP table transition
272 connectTables(deviceId, ACL_EGRESS_TABLE, JUMP_TABLE);
273
274 // for outbound table transition
275 connectTables(deviceId, STAT_OUTBOUND_TABLE, VTAP_OUTBOUND_TABLE);
276 connectTables(deviceId, VTAP_OUTBOUND_TABLE, FORWARDING_TABLE);
277
278 // for JUMP table transition
279 // we need JUMP table for bypassing routing table which contains large
280 // amount of flow rules which might cause performance degradation during
281 // table lookup
282 setupJumpTable(deviceId);
283
284 // for setting up default FLAT table behavior which is NORMAL
285 setupFlatTable(deviceId);
286 }
287
288 private void setupJumpTable(DeviceId deviceId) {
289 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
290 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
291
292 selector.matchEthDst(DEFAULT_GATEWAY_MAC);
293 treatment.transition(ROUTING_TABLE);
294
295 FlowRule flowRule = DefaultFlowRule.builder()
296 .forDevice(deviceId)
297 .withSelector(selector.build())
298 .withTreatment(treatment.build())
299 .withPriority(HIGH_PRIORITY)
300 .fromApp(appId)
301 .makePermanent()
302 .forTable(JUMP_TABLE)
303 .build();
304
305 applyRule(flowRule, true);
306
307 selector = DefaultTrafficSelector.builder();
308 treatment = DefaultTrafficTreatment.builder();
309
310 treatment.transition(STAT_OUTBOUND_TABLE);
311
312 flowRule = DefaultFlowRule.builder()
313 .forDevice(deviceId)
314 .withSelector(selector.build())
315 .withTreatment(treatment.build())
316 .withPriority(DROP_PRIORITY)
317 .fromApp(appId)
318 .makePermanent()
319 .forTable(JUMP_TABLE)
320 .build();
321
322 applyRule(flowRule, true);
323 }
324
325 private void setupFlatTable(DeviceId deviceId) {
326 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
327 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
328 .setOutput(PortNumber.NORMAL);
329
330 FlowRule flowRule = DefaultFlowRule.builder()
331 .forDevice(deviceId)
332 .withSelector(selector.build())
333 .withTreatment(treatment.build())
334 .withPriority(LOW_PRIORITY)
335 .fromApp(appId)
336 .makePermanent()
337 .forTable(FLAT_TABLE)
338 .build();
339
340 applyRule(flowRule, true);
341 }
342
343 private boolean getProviderNetworkOnlyFlag() {
344 Set<ConfigProperty> properties =
345 configService.getProperties(getClass().getName());
346 return getPropertyValueAsBoolean(properties, PROVIDER_NETWORK_ONLY);
347 }
348
349 private class InternalKubevirtNodeListener implements KubevirtNodeListener {
350
351 @Override
352 public boolean isRelevant(KubevirtNodeEvent event) {
353 return event.subject().type().equals(WORKER);
354 }
355
356 private boolean isRelevantHelper() {
357 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
358 }
359
360 @Override
361 public void event(KubevirtNodeEvent event) {
362 KubevirtNode node = event.subject();
363
364 switch (event.type()) {
365 case KUBEVIRT_NODE_COMPLETE:
366 deviceEventExecutor.execute(() -> {
367 log.info("COMPLETE node {} is detected", node.hostname());
368
369 if (!isRelevantHelper()) {
370 return;
371 }
372
373 initializePipeline(node.intgBridge());
374 });
375 break;
376 case KUBEVIRT_NODE_CREATED:
377 case KUBEVIRT_NODE_UPDATED:
378 case KUBEVIRT_NODE_REMOVED:
379 default:
380 // do nothing
381 break;
382 }
383 }
384 }
385}