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