blob: cc8574ce9c58844bc11e823ab7ed44582391a91c [file] [log] [blame]
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08001/*
Carmelo Cascone2954f132016-04-15 10:26:40 -07002 * Copyright 2016-present Open Networking Laboratory
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08003 *
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 */
16
17package org.onosproject.drivers.bmv2;
18
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080019import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
Carmelo Cascone2954f132016-04-15 10:26:40 -070021import org.apache.commons.lang3.tuple.Pair;
Carmelo Casconefbc577b2016-06-17 23:19:09 -070022import org.onlab.osgi.ServiceNotFoundException;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070023import org.onosproject.bmv2.api.context.Bmv2Configuration;
24import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
25import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
26import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
27import org.onosproject.bmv2.api.context.Bmv2Interpreter;
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -070028import org.onosproject.bmv2.api.context.Bmv2TableModel;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070029import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
30import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
Carmelo Cascone2954f132016-04-15 10:26:40 -070031import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070032import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070033import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Cascone2954f132016-04-15 10:26:40 -070034import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070035import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
36import org.onosproject.bmv2.api.service.Bmv2Controller;
37import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
38import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
Carmelo Cascone2954f132016-04-15 10:26:40 -070039import org.onosproject.net.DeviceId;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080040import org.onosproject.net.driver.AbstractHandlerBehaviour;
41import org.onosproject.net.flow.DefaultFlowEntry;
42import org.onosproject.net.flow.FlowEntry;
43import org.onosproject.net.flow.FlowRule;
44import org.onosproject.net.flow.FlowRuleProgrammable;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080045import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
47
48import java.util.Collection;
49import java.util.Collections;
50import java.util.List;
Carmelo Cascone2954f132016-04-15 10:26:40 -070051import java.util.concurrent.ConcurrentMap;
Carmelo Cascone6256d012016-06-17 13:49:52 -070052import java.util.concurrent.locks.Lock;
53import java.util.concurrent.locks.ReentrantLock;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080054
Carmelo Cascone0831efb2016-05-31 14:50:19 -070055import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*;
Carmelo Casconef8cf2882016-05-04 14:06:17 -070056import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
57
Carmelo Casconee9121642016-04-27 17:02:38 -070058/**
Carmelo Cascone0831efb2016-05-31 14:50:19 -070059 * Implementation of the flow rule programmable behaviour for BMv2.
Carmelo Casconee9121642016-04-27 17:02:38 -070060 */
Carmelo Cascone0831efb2016-05-31 14:50:19 -070061public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080062
Carmelo Cascone0831efb2016-05-31 14:50:19 -070063 private final Logger log = LoggerFactory.getLogger(this.getClass());
Carmelo Casconed4e7a772016-05-03 11:21:29 -070064
Carmelo Cascone0831efb2016-05-31 14:50:19 -070065 // Needed to synchronize operations over the same table entry.
Carmelo Cascone6256d012016-06-17 13:49:52 -070066 private static final ConcurrentMap<Bmv2TableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
Carmelo Casconed4e7a772016-05-03 11:21:29 -070067
Carmelo Cascone0831efb2016-05-31 14:50:19 -070068 private Bmv2Controller controller;
69 private Bmv2TableEntryService tableEntryService;
70 private Bmv2DeviceContextService contextService;
71
72 private boolean init() {
Carmelo Casconefbc577b2016-06-17 23:19:09 -070073 try {
74 controller = handler().get(Bmv2Controller.class);
75 tableEntryService = handler().get(Bmv2TableEntryService.class);
76 contextService = handler().get(Bmv2DeviceContextService.class);
77 return true;
78 } catch (ServiceNotFoundException e) {
79 log.warn(e.getMessage());
Carmelo Cascone0831efb2016-05-31 14:50:19 -070080 return false;
81 }
Carmelo Cascone0831efb2016-05-31 14:50:19 -070082 }
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080083
84 @Override
85 public Collection<FlowEntry> getFlowEntries() {
Carmelo Cascone2954f132016-04-15 10:26:40 -070086
Carmelo Cascone0831efb2016-05-31 14:50:19 -070087 if (!init()) {
Carmelo Casconea2f510e2016-05-03 18:36:45 -070088 return Collections.emptyList();
89 }
90
Carmelo Cascone0831efb2016-05-31 14:50:19 -070091 DeviceId deviceId = handler().data().deviceId();
92
93 Bmv2DeviceAgent deviceAgent;
94 try {
95 deviceAgent = controller.getAgent(deviceId);
96 } catch (Bmv2RuntimeException e) {
97 log.error("Failed to get BMv2 device agent: {}", e.explain());
98 return Collections.emptyList();
99 }
100
101 Bmv2DeviceContext context = contextService.getContext(deviceId);
102 if (context == null) {
103 log.warn("Unable to get device context for {}", deviceId);
tellviveks36153262017-03-07 18:16:29 +0530104 return Collections.emptyList();
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700105 }
106
107 Bmv2Interpreter interpreter = context.interpreter();
108 Bmv2Configuration configuration = context.configuration();
Carmelo Casconea2f510e2016-05-03 18:36:45 -0700109
Carmelo Cascone2954f132016-04-15 10:26:40 -0700110 List<FlowEntry> entryList = Lists.newArrayList();
111
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700112 for (Bmv2TableModel table : configuration.tables()) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700113 // For each table in the configuration AND exposed by the interpreter.
114 if (!interpreter.tableIdMap().inverse().containsKey(table.name())) {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700115 continue; // next table
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700116 }
117
Carmelo Cascone25f18882016-06-14 19:16:50 -0700118 List<Bmv2ParsedTableEntry> installedEntries;
119 try {
120 installedEntries = deviceAgent.getTableEntries(table.name());
121 } catch (Bmv2RuntimeException e) {
122 log.warn("Failed to get table entries of table {} of {}: {}", table.name(), deviceId, e.explain());
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700123 continue; // next table
Carmelo Cascone25f18882016-06-14 19:16:50 -0700124 }
125
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700126 for (Bmv2ParsedTableEntry parsedEntry : installedEntries) {
127 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, table.name(),
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700128 parsedEntry.matchKey());
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700129
Carmelo Cascone6256d012016-06-17 13:49:52 -0700130 Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, key -> new ReentrantLock());
131 lock.lock();
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700132
Carmelo Cascone6256d012016-06-17 13:49:52 -0700133 try {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700134 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700135
136 if (frWrapper == null) {
Carmelo Casconefbc577b2016-06-17 23:19:09 -0700137 log.debug("Missing reference from table entry service. Deleting it. BUG? " +
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700138 "deviceId={}, tableName={}, matchKey={}",
139 deviceId, table.name(), entryRef.matchKey());
Carmelo Casconefbc577b2016-06-17 23:19:09 -0700140 try {
141 doRemove(deviceAgent, table.name(), parsedEntry.entryId(), parsedEntry.matchKey());
142 } catch (Bmv2RuntimeException e) {
143 log.warn("Unable to remove inconsistent flow rule: {}", e.explain());
144 }
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700145 continue; // next entry
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700146 }
147
148 long remoteEntryId = parsedEntry.entryId();
149 long localEntryId = frWrapper.entryId();
150
151 if (remoteEntryId != localEntryId) {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700152 log.debug("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}",
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700153 remoteEntryId, localEntryId);
154 frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId,
Carmelo Cascone728b1cd2016-06-24 15:21:38 -0700155 frWrapper.installedOnMillis());
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700156 tableEntryService.bind(entryRef, frWrapper);
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700157 }
158
159 long bytes = 0L;
160 long packets = 0L;
161
162 if (table.hasCounters()) {
163 // Read counter values from device.
164 try {
165 Pair<Long, Long> counterValue = deviceAgent.readTableEntryCounter(table.name(),
166 remoteEntryId);
167 bytes = counterValue.getLeft();
168 packets = counterValue.getRight();
169 } catch (Bmv2RuntimeException e) {
170 log.warn("Unable to get counters for entry {}/{} of device {}: {}",
171 table.name(), remoteEntryId, deviceId, e.explain());
Carmelo Casconea2f510e2016-05-03 18:36:45 -0700172 }
173 }
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700174
175 FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
176 packets, bytes);
177 entryList.add(entry);
Carmelo Cascone6256d012016-06-17 13:49:52 -0700178
179 } finally {
180 lock.unlock();
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700181 }
182 }
183 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700184
185 return Collections.unmodifiableCollection(entryList);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800186 }
187
188 @Override
189 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800190
Carmelo Cascone2954f132016-04-15 10:26:40 -0700191 return processFlowRules(rules, Operation.APPLY);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800192 }
193
194 @Override
195 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
Carmelo Cascone2954f132016-04-15 10:26:40 -0700196
197 return processFlowRules(rules, Operation.REMOVE);
198 }
199
200 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
201
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700202 if (!init()) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800203 return Collections.emptyList();
204 }
205
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700206 DeviceId deviceId = handler().data().deviceId();
207
208 Bmv2DeviceAgent deviceAgent;
209 try {
210 deviceAgent = controller.getAgent(deviceId);
211 } catch (Bmv2RuntimeException e) {
212 log.error("Failed to get BMv2 device agent: {}", e.explain());
213 return Collections.emptyList();
214 }
215
216 Bmv2DeviceContext context = contextService.getContext(deviceId);
217 if (context == null) {
218 log.error("Unable to get device context for {}", deviceId);
219 return Collections.emptyList();
220 }
221
222 Bmv2FlowRuleTranslator translator = tableEntryService.getFlowRuleTranslator();
Carmelo Casconed4e7a772016-05-03 11:21:29 -0700223
Carmelo Cascone2954f132016-04-15 10:26:40 -0700224 List<FlowRule> processedFlowRules = Lists.newArrayList();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800225
226 for (FlowRule rule : rules) {
227
Carmelo Cascone2954f132016-04-15 10:26:40 -0700228 Bmv2TableEntry bmv2Entry;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800229
Carmelo Cascone2954f132016-04-15 10:26:40 -0700230 try {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700231 bmv2Entry = translator.translate(rule, context);
Carmelo Cascone2954f132016-04-15 10:26:40 -0700232 } catch (Bmv2FlowRuleTranslatorException e) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700233 log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700234 continue; // next rule
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800235 }
Carmelo Cascone2954f132016-04-15 10:26:40 -0700236
237 String tableName = bmv2Entry.tableName();
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700238 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
Carmelo Cascone2954f132016-04-15 10:26:40 -0700239
Carmelo Cascone6256d012016-06-17 13:49:52 -0700240 Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, k -> new ReentrantLock());
241 lock.lock();
242 try {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700243 // Get from store
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700244 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
Carmelo Cascone2954f132016-04-15 10:26:40 -0700245 try {
246 if (operation == Operation.APPLY) {
247 // Apply entry
248 long entryId;
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700249 if (frWrapper != null) {
Carmelo Casconea2f510e2016-05-03 18:36:45 -0700250 // Existing entry.
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700251 entryId = frWrapper.entryId();
252 // Tentatively delete entry before re-adding.
253 // It might not exist on device due to inconsistencies.
254 silentlyRemove(deviceAgent, entryRef.tableName(), entryId);
Carmelo Cascone2954f132016-04-15 10:26:40 -0700255 }
Carmelo Casconea2f510e2016-05-03 18:36:45 -0700256 // Add entry.
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700257 entryId = doAddEntry(deviceAgent, bmv2Entry);
Carmelo Cascone728b1cd2016-06-24 15:21:38 -0700258 frWrapper = new Bmv2FlowRuleWrapper(rule, entryId, System.currentTimeMillis());
Carmelo Cascone2954f132016-04-15 10:26:40 -0700259 } else {
260 // Remove entry
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700261 if (frWrapper == null) {
Carmelo Cascone2954f132016-04-15 10:26:40 -0700262 // Entry not found in map, how come?
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700263 forceRemove(deviceAgent, entryRef.tableName(), entryRef.matchKey());
Carmelo Cascone2954f132016-04-15 10:26:40 -0700264 } else {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700265 long entryId = frWrapper.entryId();
266 doRemove(deviceAgent, entryRef.tableName(), entryId, entryRef.matchKey());
Carmelo Cascone2954f132016-04-15 10:26:40 -0700267 }
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700268 frWrapper = null;
Carmelo Cascone2954f132016-04-15 10:26:40 -0700269 }
270 // If here, no exceptions... things went well :)
271 processedFlowRules.add(rule);
272 } catch (Bmv2RuntimeException e) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700273 log.warn("Unable to {} flow rule: {}", operation.name(), e.explain());
Carmelo Cascone2954f132016-04-15 10:26:40 -0700274 }
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700275
276 // Update entryRef binding in table entry service.
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700277 if (frWrapper != null) {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700278 tableEntryService.bind(entryRef, frWrapper);
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700279 } else {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700280 tableEntryService.unbind(entryRef);
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700281 }
Carmelo Cascone6256d012016-06-17 13:49:52 -0700282 } finally {
283 lock.unlock();
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700284 }
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800285 }
286
Carmelo Cascone2954f132016-04-15 10:26:40 -0700287 return processedFlowRules;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800288 }
289
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700290 private long doAddEntry(Bmv2DeviceAgent agent, Bmv2TableEntry entry) throws Bmv2RuntimeException {
Carmelo Casconed4e7a772016-05-03 11:21:29 -0700291 try {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700292 return agent.addTableEntry(entry);
293 } catch (Bmv2RuntimeException e) {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700294 if (e.getCode().equals(TABLE_DUPLICATE_ENTRY)) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700295 forceRemove(agent, entry.tableName(), entry.matchKey());
296 return agent.addTableEntry(entry);
297 } else {
298 throw e;
299 }
Carmelo Casconed4e7a772016-05-03 11:21:29 -0700300 }
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700301 }
Carmelo Casconed4e7a772016-05-03 11:21:29 -0700302
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700303 private void doRemove(Bmv2DeviceAgent agent, String tableName, long entryId, Bmv2MatchKey matchKey)
304 throws Bmv2RuntimeException {
305 try {
306 agent.deleteTableEntry(tableName, entryId);
307 } catch (Bmv2RuntimeException e) {
Carmelo Casconeee4cd7e2016-06-16 18:28:43 -0700308 if (e.getCode().equals(TABLE_INVALID_HANDLE) || e.getCode().equals(TABLE_EXPIRED_HANDLE)) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700309 // entry is not there with the declared ID, try with a forced remove.
310 forceRemove(agent, tableName, matchKey);
311 } else {
312 throw e;
313 }
314 }
315 }
316
317 private void forceRemove(Bmv2DeviceAgent agent, String tableName, Bmv2MatchKey matchKey)
318 throws Bmv2RuntimeException {
319 // Find the entryID (expensive call!)
Carmelo Cascone25f18882016-06-14 19:16:50 -0700320 for (Bmv2ParsedTableEntry pEntry : agent.getTableEntries(tableName)) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700321 if (pEntry.matchKey().equals(matchKey)) {
322 // Remove entry and drop exceptions.
323 silentlyRemove(agent, tableName, pEntry.entryId());
324 break;
325 }
326 }
327 }
328
329 private void silentlyRemove(Bmv2DeviceAgent agent, String tableName, long entryId) {
330 try {
331 agent.deleteTableEntry(tableName, entryId);
332 } catch (Bmv2RuntimeException e) {
333 // do nothing
334 }
Carmelo Casconed4e7a772016-05-03 11:21:29 -0700335 }
336
Carmelo Cascone2954f132016-04-15 10:26:40 -0700337 private enum Operation {
338 APPLY, REMOVE
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800339 }
tellviveks36153262017-03-07 18:16:29 +0530340}