blob: c226e60f0b94636e7d4c6e5c6dfdc34c3d4b9fb7 [file] [log] [blame]
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08001/*
2 * Copyright 2014-2016 Open Networking Laboratory
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 */
16
17package org.onosproject.drivers.bmv2;
18
19import com.google.common.base.Preconditions;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070023import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
24import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
25import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
26import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080027import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
28import org.onosproject.net.driver.AbstractHandlerBehaviour;
29import org.onosproject.net.flow.DefaultFlowEntry;
30import org.onosproject.net.flow.FlowEntry;
31import org.onosproject.net.flow.FlowRule;
32import org.onosproject.net.flow.FlowRuleProgrammable;
33import org.onosproject.net.flow.criteria.Criterion;
34import org.onosproject.net.flow.criteria.ExtensionCriterion;
35import org.onosproject.net.flow.criteria.ExtensionSelector;
36import org.onosproject.net.flow.criteria.ExtensionSelectorType;
37import org.onosproject.net.flow.instructions.ExtensionTreatment;
38import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
39import org.onosproject.net.flow.instructions.Instruction;
40import org.onosproject.net.flow.instructions.Instructions;
41import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44import java.util.Collection;
45import java.util.Collections;
46import java.util.List;
47import java.util.Map;
48import java.util.Set;
49
50public class Bmv2FlowRuleDriver extends AbstractHandlerBehaviour
51 implements FlowRuleProgrammable {
52
53 private final Logger log =
54 LoggerFactory.getLogger(this.getClass());
55
56 // Bmv2 doesn't support proper table dump, use a local store
57 // FIXME: synchronize entries with device
58 private final Map<FlowRule, FlowEntry> deviceEntriesMap = Maps.newHashMap();
59 private final Map<Integer, Set<FlowRule>> tableRulesMap = Maps.newHashMap();
60 private final Map<FlowRule, Long> tableEntryIdsMap = Maps.newHashMap();
61
62 @Override
63 public Collection<FlowEntry> getFlowEntries() {
64 return Collections.unmodifiableCollection(
65 deviceEntriesMap.values());
66 }
67
68 @Override
69 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
70 Bmv2ThriftClient deviceClient;
71 try {
72 deviceClient = getDeviceClient();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070073 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080074 return Collections.emptyList();
75 }
76
77 List<FlowRule> appliedFlowRules = Lists.newArrayList();
78
79 for (FlowRule rule : rules) {
80
81 Bmv2TableEntry entry;
82
83 try {
84 entry = parseFlowRule(rule);
85 } catch (IllegalStateException e) {
86 log.error("Unable to parse flow rule", e);
87 continue;
88 }
89
90 // Instantiate flowrule set for table if it does not exist
91 if (!tableRulesMap.containsKey(rule.tableId())) {
92 tableRulesMap.put(rule.tableId(), Sets.newHashSet());
93 }
94
95 if (tableRulesMap.get(rule.tableId()).contains(rule)) {
96 /* Rule is already installed in the table */
97 long entryId = tableEntryIdsMap.get(rule);
98
99 try {
100 deviceClient.modifyTableEntry(
101 entry.tableName(), entryId, entry.action());
102
103 // Replace stored rule as treatment, etc. might have changed
104 // Java Set doesn't replace on add, remove first
105 tableRulesMap.get(rule.tableId()).remove(rule);
106 tableRulesMap.get(rule.tableId()).add(rule);
107 tableEntryIdsMap.put(rule, entryId);
108 deviceEntriesMap.put(rule, new DefaultFlowEntry(
109 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700110 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800111 log.error("Unable to update flow rule", e);
112 continue;
113 }
114
115 } else {
116 /* Rule is new */
117 try {
118 long entryId = deviceClient.addTableEntry(entry);
119
120 tableRulesMap.get(rule.tableId()).add(rule);
121 tableEntryIdsMap.put(rule, entryId);
122 deviceEntriesMap.put(rule, new DefaultFlowEntry(
123 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700124 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800125 log.error("Unable to add flow rule", e);
126 continue;
127 }
128 }
129
130 appliedFlowRules.add(rule);
131 }
132
133 return Collections.unmodifiableCollection(appliedFlowRules);
134 }
135
136 @Override
137 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
138 Bmv2ThriftClient deviceClient;
139 try {
140 deviceClient = getDeviceClient();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700141 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800142 return Collections.emptyList();
143 }
144
145 List<FlowRule> removedFlowRules = Lists.newArrayList();
146
147 for (FlowRule rule : rules) {
148
149 if (tableEntryIdsMap.containsKey(rule)) {
150 long entryId = tableEntryIdsMap.get(rule);
151 String tableName = parseTableName(rule.tableId());
152
153 try {
154 deviceClient.deleteTableEntry(tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700155 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800156 log.error("Unable to delete flow rule", e);
157 continue;
158 }
159
160 /* remove from local store */
161 tableEntryIdsMap.remove(rule);
162 tableRulesMap.get(rule.tableId()).remove(rule);
163 deviceEntriesMap.remove(rule);
164
165 removedFlowRules.add(rule);
166 }
167 }
168
169 return Collections.unmodifiableCollection(removedFlowRules);
170 }
171
172 private Bmv2TableEntry parseFlowRule(FlowRule flowRule) {
173
174 // TODO make it pipeline dependant, i.e. implement mapping
175
176 Bmv2TableEntry.Builder entryBuilder = Bmv2TableEntry.builder();
177
178 // Check selector
179 ExtensionCriterion ec =
180 (ExtensionCriterion) flowRule
181 .selector().getCriterion(Criterion.Type.EXTENSION);
182 Preconditions.checkState(
183 flowRule.selector().criteria().size() == 1
184 && ec != null,
185 "Selector must have only 1 criterion of type EXTENSION");
186 ExtensionSelector es = ec.extensionSelector();
187 Preconditions.checkState(
188 es.type() == ExtensionSelectorType.ExtensionSelectorTypes.P4_BMV2_MATCH_KEY.type(),
189 "ExtensionSelectorType must be P4_BMV2_MATCH_KEY");
190
191 // Selector OK, get Bmv2MatchKey
192 entryBuilder.withMatchKey(((Bmv2ExtensionSelector) es).matchKey());
193
194 // Check treatment
195 Instruction inst = flowRule.treatment().allInstructions().get(0);
196 Preconditions.checkState(
197 flowRule.treatment().allInstructions().size() == 1
198 && inst.type() == Instruction.Type.EXTENSION,
199 "Treatment must have only 1 instruction of type EXTENSION");
200 ExtensionTreatment et =
201 ((Instructions.ExtensionInstructionWrapper) inst)
202 .extensionInstruction();
203
204 Preconditions.checkState(
205 et.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.P4_BMV2_ACTION.type(),
206 "ExtensionTreatmentType must be P4_BMV2_ACTION");
207
208 // Treatment OK, get Bmv2Action
209 entryBuilder.withAction(((Bmv2ExtensionTreatment) et).getAction());
210
211 // Table name
212 entryBuilder.withTableName(parseTableName(flowRule.tableId()));
213
214 if (!flowRule.isPermanent()) {
215 entryBuilder.withTimeout(flowRule.timeout());
216 }
217
218 entryBuilder.withPriority(flowRule.priority());
219
220 return entryBuilder.build();
221 }
222
223 private String parseTableName(int tableId) {
224 // TODO: map tableId with tableName according to P4 JSON
225 return "table" + String.valueOf(tableId);
226 }
227
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700228 private Bmv2ThriftClient getDeviceClient() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800229 try {
230 return Bmv2ThriftClient.of(handler().data().deviceId());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700231 } catch (Bmv2RuntimeException e) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800232 log.error("Failed to connect to Bmv2 device", e);
233 throw e;
234 }
235 }
236}