blob: a54a3c89edc5f7a7333f7f1de90863aa8b0e3a4a [file] [log] [blame]
Carmelo Casconeb7388bd2016-04-14 10:20:13 -07001/*
2 * Copyright 2016-present 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.translators;
18
19import com.google.common.annotations.Beta;
20import org.onlab.util.ImmutableByteSequence;
21import org.onosproject.bmv2.api.model.Bmv2Model;
22import org.onosproject.bmv2.api.model.Bmv2ModelField;
23import org.onosproject.bmv2.api.model.Bmv2ModelTable;
24import org.onosproject.bmv2.api.model.Bmv2ModelTableKey;
25import org.onosproject.bmv2.api.runtime.Bmv2Action;
26import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
27import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
28import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
29import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
30import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
31import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.TrafficSelector;
34import org.onosproject.net.flow.TrafficTreatment;
35import org.onosproject.net.flow.criteria.Criterion;
36import org.onosproject.net.flow.criteria.EthCriterion;
37import org.onosproject.net.flow.criteria.EthTypeCriterion;
38import org.onosproject.net.flow.criteria.ExtensionCriterion;
39import org.onosproject.net.flow.criteria.ExtensionSelector;
40import org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes;
41import org.onosproject.net.flow.criteria.PortCriterion;
42import org.onosproject.net.flow.instructions.ExtensionTreatment;
43import org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes;
44import org.onosproject.net.flow.instructions.Instruction;
45import org.onosproject.net.flow.instructions.Instructions;
46import org.onosproject.net.flow.instructions.Instructions.ExtensionInstructionWrapper;
47
48/**
49 * Default Bmv2 flow rule translator implementation.
50 * <p>
51 * Flow rules are translated into {@link Bmv2TableEntry BMv2 table entries} according to the following logic:
52 * <ul>
53 * <li> table name: obtained from the Bmv2 model using the flow rule table ID;
54 * <li> match key: if the flow rule selector defines only a criterion of type {@link Criterion.Type#EXTENSION EXTENSION}
55 * , then the latter is expected to contain a {@link Bmv2ExtensionSelector Bmv2ExtensionSelector}, which should provide
56 * a match key already formatted for the given table; otherwise a match key is built using the
57 * {@link TranslatorConfig#fieldToCriterionTypeMap() mapping} defined by this translator configuration.
58 * <li> action: if the flow rule treatment contains only one instruction of type
59 * {@link Instruction.Type#EXTENSION EXTENSION}, then the latter is expected to contain a {@link Bmv2ExtensionTreatment}
60 * , which should provide a {@link Bmv2Action} already formatted for the given table; otherwise, an action is
61 * {@link TranslatorConfig#buildAction(TrafficTreatment) built} using this translator configuration.
62 * <li> priority: the same as the flow rule.
63 * <li> timeout: if the table supports timeout, use the same as the flow rule, otherwise none (i.e. permanent entry).
64 * </ul>
65 */
66@Beta
67public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
68
69 // TODO: config is harcoded now, instead it should be selected based on device model
Carmelo Casconee9121642016-04-27 17:02:38 -070070 private final TranslatorConfig config = new Bmv2SimpleTranslatorConfig();
Carmelo Casconeb7388bd2016-04-14 10:20:13 -070071 private final Bmv2Model model = config.model();
72
73 private static Bmv2TernaryMatchParam buildTernaryParam(Bmv2ModelField field, Criterion criterion, int byteWidth)
74 throws Bmv2FlowRuleTranslatorException {
75
76 // Value and mask will be filled according to criterion type
77 ImmutableByteSequence value;
78 ImmutableByteSequence mask = null;
79
80 switch (criterion.type()) {
81 case IN_PORT:
82 // FIXME: allow port numbers of variable bit length (based on model), truncating when necessary
83 short port = (short) ((PortCriterion) criterion).port().toLong();
84 value = ImmutableByteSequence.copyFrom(port);
85 break;
86 case ETH_DST:
87 EthCriterion c = (EthCriterion) criterion;
88 value = ImmutableByteSequence.copyFrom(c.mac().toBytes());
89 if (c.mask() != null) {
90 mask = ImmutableByteSequence.copyFrom(c.mask().toBytes());
91 }
92 break;
93 case ETH_SRC:
94 EthCriterion c2 = (EthCriterion) criterion;
95 value = ImmutableByteSequence.copyFrom(c2.mac().toBytes());
96 if (c2.mask() != null) {
97 mask = ImmutableByteSequence.copyFrom(c2.mask().toBytes());
98 }
99 break;
100 case ETH_TYPE:
101 short ethType = ((EthTypeCriterion) criterion).ethType().toShort();
102 value = ImmutableByteSequence.copyFrom(ethType);
103 break;
104 // TODO: implement building for other criterion types (easy with DefaultCriterion of ONOS-4034)
105 default:
106 throw new Bmv2FlowRuleTranslatorException("Feature not implemented, ternary builder for criterion" +
107 "type: " + criterion.type().name());
108 }
109
110 if (mask == null) {
111 // no mask, all ones
112 mask = ImmutableByteSequence.ofOnes(byteWidth);
113 }
114
115 return new Bmv2TernaryMatchParam(value, mask);
116 }
117
118 private static Bmv2MatchKey getMatchKeyFromExtension(ExtensionCriterion criterion)
119 throws Bmv2FlowRuleTranslatorException {
120
121 ExtensionSelector extSelector = criterion.extensionSelector();
122
123 if (extSelector.type() == ExtensionSelectorTypes.P4_BMV2_MATCH_KEY.type()) {
124 if (extSelector instanceof Bmv2ExtensionSelector) {
125 return ((Bmv2ExtensionSelector) extSelector).matchKey();
126 } else {
127 throw new Bmv2FlowRuleTranslatorException("Unable to decode extension selector " + extSelector);
128 }
129 } else {
130 throw new Bmv2FlowRuleTranslatorException("Unsupported extension selector type " + extSelector.type());
131 }
132 }
133
134 private static Bmv2Action getActionFromExtension(Instructions.ExtensionInstructionWrapper inst)
135 throws Bmv2FlowRuleTranslatorException {
136
137 ExtensionTreatment extTreatment = inst.extensionInstruction();
138
139 if (extTreatment.type() == ExtensionTreatmentTypes.P4_BMV2_ACTION.type()) {
140 if (extTreatment instanceof Bmv2ExtensionTreatment) {
141 return ((Bmv2ExtensionTreatment) extTreatment).getAction();
142 } else {
143 throw new Bmv2FlowRuleTranslatorException("Unable to decode treatment extension: " + extTreatment);
144 }
145 } else {
146 throw new Bmv2FlowRuleTranslatorException("Unsupported treatment extension type: " + extTreatment.type());
147 }
148 }
149
150 private static Bmv2MatchKey buildMatchKey(TranslatorConfig config, TrafficSelector selector, Bmv2ModelTable table)
151 throws Bmv2FlowRuleTranslatorException {
152
153 Bmv2MatchKey.Builder matchKeyBuilder = Bmv2MatchKey.builder();
154
155 for (Bmv2ModelTableKey key : table.keys()) {
156
157 String fieldName = key.field().header().name() + "." + key.field().type().name();
158 int byteWidth = (int) Math.ceil((double) key.field().type().bitWidth() / 8.0);
159 Criterion.Type criterionType = config.fieldToCriterionTypeMap().get(fieldName);
160
161 if (criterionType == null || selector.getCriterion(criterionType) == null) {
162 // A mapping is not available or the selector doesn't have such a type
163 switch (key.matchType()) {
164 case TERNARY:
165 // Wildcard field
166 matchKeyBuilder.withWildcard(byteWidth);
167 break;
168 case LPM:
169 // LPM with prefix 0
170 matchKeyBuilder.add(new Bmv2LpmMatchParam(ImmutableByteSequence.ofZeros(byteWidth), 0));
171 break;
172 default:
173 throw new Bmv2FlowRuleTranslatorException("Match field not supported: " + fieldName);
174 }
175 // Next key
176 continue;
177 }
178
179 Criterion criterion = selector.getCriterion(criterionType);
180 Bmv2TernaryMatchParam matchParam = null;
181
182 switch (key.matchType()) {
183 case TERNARY:
184 matchParam = buildTernaryParam(key.field(), criterion, byteWidth);
185 break;
186 default:
187 // TODO: implement other match param builders (exact, LPM, etc.)
188 throw new Bmv2FlowRuleTranslatorException("Feature not implemented, match param builder: "
189 + key.matchType().name());
190 }
191
192 matchKeyBuilder.add(matchParam);
193 }
194
195 return matchKeyBuilder.build();
196 }
197
198 @Override
199 public Bmv2TableEntry translate(FlowRule rule)
200 throws Bmv2FlowRuleTranslatorException {
201
202 int tableId = rule.tableId();
203
204 Bmv2ModelTable table = model.table(tableId);
205
206 if (table == null) {
207 throw new Bmv2FlowRuleTranslatorException("Unknown table ID: " + tableId);
208 }
209
210 /* Translate selector */
211
212 TrafficSelector selector = rule.selector();
213 Bmv2MatchKey bmv2MatchKey = null;
214
215 // If selector has only 1 criterion of type extension, use that
216 Criterion criterion = selector.getCriterion(Criterion.Type.EXTENSION);
217 if (criterion != null) {
218 if (selector.criteria().size() == 1) {
219 bmv2MatchKey = getMatchKeyFromExtension((ExtensionCriterion) criterion);
220 } else {
221 throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic selector, found multiple " +
222 "criteria of which one is an extension: " +
223 selector.toString());
224 }
225 }
226
227 if (bmv2MatchKey == null) {
228 // not an extension
229 bmv2MatchKey = buildMatchKey(config, selector, table);
230 }
231
232 /* Translate treatment */
233
234 TrafficTreatment treatment = rule.treatment();
235 Bmv2Action bmv2Action = null;
236
237 // If treatment has only 1 instruction of type extension, use that
238 for (Instruction inst : treatment.allInstructions()) {
239 if (inst.type() == Instruction.Type.EXTENSION) {
240 if (treatment.allInstructions().size() == 1) {
241 bmv2Action = getActionFromExtension((ExtensionInstructionWrapper) inst);
242 } else {
243 throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic treatment, found multiple " +
244 "instructions of which one is an extension: " +
245 selector.toString());
246 }
247 }
248 }
249
250 if (bmv2Action == null) {
251 // No extension, use config to build action
252 bmv2Action = config.buildAction(treatment);
253 }
254
255 if (bmv2Action == null) {
256 // Config returned null
257 throw new Bmv2FlowRuleTranslatorException("Unable to translate treatment: " + treatment);
258 }
259
260 Bmv2TableEntry.Builder tableEntryBuilder = Bmv2TableEntry.builder();
261
262 tableEntryBuilder
263 .withTableName(table.name())
264 .withPriority(rule.priority())
265 .withMatchKey(bmv2MatchKey)
266 .withAction(bmv2Action);
267
268 if (!rule.isPermanent()) {
269 if (table.hasTimeouts()) {
270 tableEntryBuilder.withTimeout((double) rule.timeout());
271 }
272 //FIXME: add warn log or exception?
273 }
274
275 return tableEntryBuilder.build();
276 }
277
278 @Override
279 public TranslatorConfig config() {
280 return this.config;
281 }
282}