blob: 6f61c3b6f91cf071810bb59d24e8004d8cb5d791 [file] [log] [blame]
Carmelo Cascone00a59962017-06-16 17:51:49 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Carmelo Cascone00a59962017-06-16 17:51:49 +09003 *
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.net.pi.impl;
18
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
21import org.onlab.util.ImmutableByteSequence;
22import org.onosproject.net.Device;
23import org.onosproject.net.flow.FlowRule;
Carmelo Cascone00a59962017-06-16 17:51:49 +090024import org.onosproject.net.flow.TrafficSelector;
25import org.onosproject.net.flow.TrafficTreatment;
26import org.onosproject.net.flow.criteria.Criterion;
27import org.onosproject.net.flow.criteria.PiCriterion;
28import org.onosproject.net.flow.instructions.Instruction;
29import org.onosproject.net.flow.instructions.PiInstruction;
30import org.onosproject.net.pi.model.PiActionModel;
31import org.onosproject.net.pi.model.PiActionParamModel;
Carmelo Cascone87892e22017-11-13 16:01:29 -080032import org.onosproject.net.pi.model.PiMatchFieldId;
33import org.onosproject.net.pi.model.PiMatchFieldModel;
Carmelo Cascone00a59962017-06-16 17:51:49 +090034import org.onosproject.net.pi.model.PiPipeconf;
35import org.onosproject.net.pi.model.PiPipelineInterpreter;
36import org.onosproject.net.pi.model.PiPipelineModel;
Carmelo Cascone87892e22017-11-13 16:01:29 -080037import org.onosproject.net.pi.model.PiTableId;
Carmelo Cascone00a59962017-06-16 17:51:49 +090038import org.onosproject.net.pi.model.PiTableModel;
39import org.onosproject.net.pi.runtime.PiAction;
40import org.onosproject.net.pi.runtime.PiActionParam;
41import org.onosproject.net.pi.runtime.PiExactFieldMatch;
42import org.onosproject.net.pi.runtime.PiFieldMatch;
Carmelo Cascone00a59962017-06-16 17:51:49 +090043import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
Carmelo Cascone0e896a02017-07-31 07:22:27 +020044import org.onosproject.net.pi.runtime.PiMatchKey;
Carmelo Cascone00a59962017-06-16 17:51:49 +090045import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
46import org.onosproject.net.pi.runtime.PiTableAction;
47import org.onosproject.net.pi.runtime.PiTableEntry;
Carmelo Cascone00a59962017-06-16 17:51:49 +090048import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
49import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.util.Collection;
53import java.util.Map;
54import java.util.Optional;
55import java.util.Set;
56import java.util.StringJoiner;
57
58import static java.lang.String.format;
59import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
60import static org.onlab.util.ImmutableByteSequence.fit;
61import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
62import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020063import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
64import static org.onosproject.net.pi.impl.PiUtils.translateTableId;
65import static org.onosproject.net.pi.runtime.PiTranslationService.PiTranslationException;
Carmelo Cascone00a59962017-06-16 17:51:49 +090066
67/**
68 * Implementation of flow rule translation logic.
69 */
70final class PiFlowRuleTranslator {
71
Carmelo Casconea3d811c2017-08-22 01:03:45 +020072 public static final int MAX_PI_PRIORITY = (int) Math.pow(2, 24);
Carmelo Cascone87b9b392017-10-02 18:33:20 +020073 private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslator.class);
Carmelo Cascone00a59962017-06-16 17:51:49 +090074
75 private PiFlowRuleTranslator() {
76 // Hide constructor.
77 }
78
Carmelo Cascone87b9b392017-10-02 18:33:20 +020079 /**
80 * Returns a PI table entry equivalent to the given flow rule, for the given pipeconf and device.
81 *
82 * @param rule flow rule
83 * @param pipeconf pipeconf
84 * @param device device
85 * @return PI table entry
86 * @throws PiTranslationException if the flow rule cannot be translated
87 */
88 static PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf, Device device)
89 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +090090
91 PiPipelineModel pipelineModel = pipeconf.pipelineModel();
92
93 // Retrieve interpreter, if any.
Carmelo Cascone87b9b392017-10-02 18:33:20 +020094 final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
95 // Get table model.
96 final PiTableId piTableId = translateTableId(rule.table(), interpreter);
97 final PiTableModel tableModel = getTableModel(piTableId, pipelineModel);
98 // Translate selector.
99 final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(interpreter, rule.selector(), tableModel);
100 // Translate treatment.
101 final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
Carmelo Casconea62ac3d2017-08-30 03:19:00 +0200102
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200103 // Build PI entry.
104 final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900105
Carmelo Casconea3d811c2017-08-22 01:03:45 +0200106 // In the P4 world 0 is the highest priority, in ONOS the lowest one.
107 // FIXME: move priority conversion to the driver, where different constraints might apply
108 // e.g. less bits for encoding priority in TCAM-based implementations.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200109 final int newPriority;
Carmelo Casconea3d811c2017-08-22 01:03:45 +0200110 if (rule.priority() > MAX_PI_PRIORITY) {
111 log.warn("Flow rule priority too big, setting translated priority to max value {}: {}",
112 MAX_PI_PRIORITY, rule);
113 newPriority = 0;
114 } else {
115 newPriority = MAX_PI_PRIORITY - rule.priority();
116 }
Carmelo Cascone00a59962017-06-16 17:51:49 +0900117
118 tableEntryBuilder
119 .forTable(piTableId)
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200120 .withPriority(newPriority)
Carmelo Cascone0e896a02017-07-31 07:22:27 +0200121 .withMatchKey(PiMatchKey.builder()
122 .addFieldMatches(fieldMatches)
123 .build())
Yi Tseng82512da2017-08-16 19:46:36 -0700124 .withAction(piTableAction);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900125
126 if (!rule.isPermanent()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200127 if (tableModel.supportsAging()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900128 tableEntryBuilder.withTimeout((double) rule.timeout());
129 } else {
130 log.warn("Flow rule is temporary, but table '{}' doesn't support " +
Carmelo Cascone87892e22017-11-13 16:01:29 -0800131 "aging, translating to permanent.", tableModel.id());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900132 }
133
134 }
135
136 return tableEntryBuilder.build();
137 }
138
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200139
140 /**
141 * Returns a PI action equivalent to the given treatment, optionally using the given interpreter. This method also
142 * checks that the produced PI table action is suitable for the given table ID and pipeline model. If suitable, the
143 * returned action instance will have parameters well-sized, according to the table model.
144 *
145 * @param treatment traffic treatment
146 * @param interpreter interpreter
147 * @param tableId PI table ID
148 * @param pipelineModel pipeline model
149 * @return PI table action
150 * @throws PiTranslationException if the treatment cannot be translated or if the PI action is not suitable for the
151 * given pipeline model
152 */
153 static PiTableAction translateTreatment(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
154 PiTableId tableId, PiPipelineModel pipelineModel)
155 throws PiTranslationException {
156 PiTableModel tableModel = getTableModel(tableId, pipelineModel);
157 return typeCheckAction(buildAction(treatment, interpreter, tableId), tableModel);
158 }
159
160 private static PiTableModel getTableModel(PiTableId piTableId, PiPipelineModel pipelineModel)
161 throws PiTranslationException {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800162 return pipelineModel.table(piTableId)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200163 .orElseThrow(() -> new PiTranslationException(format(
164 "Not such a table in pipeline model: %s", piTableId)));
165 }
166
Carmelo Cascone00a59962017-06-16 17:51:49 +0900167 /**
168 * Builds a PI action out of the given treatment, optionally using the given interpreter.
169 */
Yi Tseng82512da2017-08-16 19:46:36 -0700170 private static PiTableAction buildAction(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200171 PiTableId tableId)
172 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900173
174 PiTableAction piTableAction = null;
175
176 // If treatment has only one instruction of type PiInstruction, use that.
177 for (Instruction inst : treatment.allInstructions()) {
178 if (inst.type() == Instruction.Type.PROTOCOL_INDEPENDENT) {
179 if (treatment.allInstructions().size() == 1) {
180 piTableAction = ((PiInstruction) inst).action();
181 } else {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200182 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900183 "Unable to translate treatment, found multiple instructions " +
184 "of which one is protocol-independent: %s", treatment));
185 }
186 }
187 }
188
189 if (piTableAction == null && interpreter != null) {
190 // No PiInstruction, use interpreter to build action.
191 try {
Carmelo Casconef3a1a382017-07-27 12:04:39 -0400192 piTableAction = interpreter.mapTreatment(treatment, tableId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900193 } catch (PiPipelineInterpreter.PiInterpreterException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200194 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900195 "Interpreter was unable to translate treatment. " + e.getMessage());
196 }
197 }
198
199 if (piTableAction == null) {
200 // No PiInstruction, no interpreter. It's time to give up.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200201 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900202 "Unable to translate treatment, neither an interpreter or a "
203 + "protocol-independent instruction were provided.");
204 }
205
Yi Tseng82512da2017-08-16 19:46:36 -0700206 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900207 }
208
Yi Tseng82512da2017-08-16 19:46:36 -0700209 private static PiTableAction typeCheckAction(PiTableAction piTableAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200210 throws PiTranslationException {
Yi Tseng82512da2017-08-16 19:46:36 -0700211 switch (piTableAction.type()) {
212 case ACTION:
213 return checkPiAction((PiAction) piTableAction, table);
214 default:
215 // FIXME: should we check? how?
216 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900217
Yi Tseng82512da2017-08-16 19:46:36 -0700218 }
219 }
220
221 private static PiTableAction checkPiAction(PiAction piAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200222 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900223 // Table supports this action?
Carmelo Cascone87892e22017-11-13 16:01:29 -0800224 PiActionModel actionModel = table.action(piAction.id()).orElseThrow(
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200225 () -> new PiTranslationException(format("Not such action '%s' for table '%s'",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800226 piAction.id(), table.id())));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900227
228 // Is the number of runtime parameters correct?
229 if (actionModel.params().size() != piAction.parameters().size()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200230 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900231 "Wrong number of runtime parameters for action '%s', expected %d but found %d",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800232 actionModel.id(), actionModel.params().size(), piAction.parameters().size()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900233 }
234
235 // Forge a new action instance with well-sized parameters.
236 // The same comment as in typeCheckFieldMatch() about duplicating field match instances applies here.
237 PiAction.Builder newActionBuilder = PiAction.builder().withId(piAction.id());
238 for (PiActionParam param : piAction.parameters()) {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800239 PiActionParamModel paramModel = actionModel.param(param.id())
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200240 .orElseThrow(() -> new PiTranslationException(format(
Carmelo Cascone87892e22017-11-13 16:01:29 -0800241 "Not such parameter '%s' for action '%s'", param.id(), actionModel)));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900242 try {
243 newActionBuilder.withParameter(new PiActionParam(param.id(),
244 fit(param.value(), paramModel.bitWidth())));
245 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200246 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900247 "Size mismatch for parameter '%s' of action '%s': %s",
248 param.id(), piAction.id(), e.getMessage()));
249 }
250 }
251
252 return newActionBuilder.build();
253 }
254
255 /**
256 * Builds a collection of PI field matches out of the given selector, optionally using the given interpreter. The
257 * field matches returned are guaranteed to be compatible for the given table model.
258 */
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200259 private static Collection<PiFieldMatch> translateFieldMatches(PiPipelineInterpreter interpreter,
260 TrafficSelector selector, PiTableModel tableModel)
261 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900262
Carmelo Cascone87892e22017-11-13 16:01:29 -0800263 Map<PiMatchFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900264
265 // If present, find a PiCriterion and get its field matches as a map. Otherwise, use an empty map.
Carmelo Cascone87892e22017-11-13 16:01:29 -0800266 Map<PiMatchFieldId, PiFieldMatch> piCriterionFields = selector.criteria().stream()
Carmelo Cascone00a59962017-06-16 17:51:49 +0900267 .filter(c -> c.type().equals(PROTOCOL_INDEPENDENT))
268 .map(c -> (PiCriterion) c)
269 .findFirst()
270 .map(PiCriterion::fieldMatches)
271 .map(c -> {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800272 Map<PiMatchFieldId, PiFieldMatch> fieldMap = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900273 c.forEach(fieldMatch -> fieldMap.put(fieldMatch.fieldId(), fieldMatch));
274 return fieldMap;
275 })
276 .orElse(Maps.newHashMap());
277
278 Set<Criterion> translatedCriteria = Sets.newHashSet();
279 Set<Criterion> ignoredCriteria = Sets.newHashSet();
Carmelo Cascone87892e22017-11-13 16:01:29 -0800280 Set<PiMatchFieldId> usedPiCriterionFields = Sets.newHashSet();
281 Set<PiMatchFieldId> ignoredPiCriterionFields = Sets.newHashSet();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900282
Carmelo Cascone87892e22017-11-13 16:01:29 -0800283 for (PiMatchFieldModel fieldModel : tableModel.matchFields()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900284
Carmelo Cascone87892e22017-11-13 16:01:29 -0800285 PiMatchFieldId fieldId = fieldModel.id();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900286
Carmelo Cascone87892e22017-11-13 16:01:29 -0800287 int bitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900288 int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);
289
290 Optional<Criterion.Type> criterionType =
291 interpreter == null
292 ? Optional.empty()
Carmelo Cascone87892e22017-11-13 16:01:29 -0800293 : interpreter.mapPiMatchFieldId(fieldId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900294
295 Criterion criterion = criterionType.map(selector::getCriterion).orElse(null);
296
297 if (!piCriterionFields.containsKey(fieldId) && criterion == null) {
298 // Neither a field in PiCriterion is available nor a Criterion mapping is possible.
299 // Can ignore if the match is ternary or LPM.
300 switch (fieldModel.matchType()) {
301 case TERNARY:
302 // Wildcard the whole field.
303 fieldMatches.put(fieldId, new PiTernaryFieldMatch(
304 fieldId,
305 ImmutableByteSequence.ofZeros(fieldByteWidth),
306 ImmutableByteSequence.ofZeros(fieldByteWidth)));
307 break;
308 case LPM:
309 // LPM with prefix 0
310 fieldMatches.put(fieldId, new PiLpmFieldMatch(fieldId,
311 ImmutableByteSequence.ofZeros(fieldByteWidth),
312 0));
313 break;
314 // FIXME: Can we handle the case of RANGE or VALID match?
315 default:
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200316 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900317 "No value found for required match field '%s'", fieldId));
318 }
319 // Next field.
320 continue;
321 }
322
323 PiFieldMatch fieldMatch = null;
324
325 if (criterion != null) {
326 // Criterion mapping is possible for this field id.
327 try {
328 fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
329 translatedCriteria.add(criterion);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200330 } catch (PiTranslationException ex) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900331 // Ignore exception if the same field was found in PiCriterion.
332 if (piCriterionFields.containsKey(fieldId)) {
333 ignoredCriteria.add(criterion);
334 } else {
335 throw ex;
336 }
337 }
338 }
339
340 if (piCriterionFields.containsKey(fieldId)) {
341 // Field was found in PiCriterion.
342 if (fieldMatch != null) {
343 // Field was already translated from other criterion.
344 // Throw exception only if we are trying to match on different values of the same field...
345 if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200346 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900347 "Duplicate match field '%s': instance translated from criterion '%s' is different to " +
348 "what found in PiCriterion.", fieldId, criterion.type()));
349 }
350 ignoredPiCriterionFields.add(fieldId);
351 } else {
352 fieldMatch = piCriterionFields.get(fieldId);
353 fieldMatch = typeCheckFieldMatch(fieldMatch, fieldModel);
354 usedPiCriterionFields.add(fieldId);
355 }
356 }
357
358 fieldMatches.put(fieldId, fieldMatch);
359 }
360
361 // Check if all criteria have been translated.
362 StringJoiner skippedCriteriaJoiner = new StringJoiner(", ");
363 selector.criteria().stream()
364 .filter(c -> !c.type().equals(PROTOCOL_INDEPENDENT))
365 .filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c))
366 .forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
367 if (skippedCriteriaJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200368 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900369 "The following criteria cannot be translated for table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800370 tableModel.id(), skippedCriteriaJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900371 }
372
373 // Check if all fields found in PiCriterion have been used.
374 StringJoiner skippedPiFieldsJoiner = new StringJoiner(", ");
375 piCriterionFields.keySet().stream()
376 .filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k))
377 .forEach(k -> skippedPiFieldsJoiner.add(k.id()));
378 if (skippedPiFieldsJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200379 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900380 "The following PiCriterion field matches are not supported in table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800381 tableModel.id(), skippedPiFieldsJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900382 }
383
384 return fieldMatches.values();
385 }
386
Carmelo Cascone87892e22017-11-13 16:01:29 -0800387 private static PiFieldMatch typeCheckFieldMatch(PiFieldMatch fieldMatch, PiMatchFieldModel fieldModel)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200388 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900389
390 // Check parameter type and size
391 if (!fieldModel.matchType().equals(fieldMatch.type())) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200392 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900393 "Wrong match type for field '%s', expected %s, but found %s",
394 fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
395 }
396
Carmelo Cascone87892e22017-11-13 16:01:29 -0800397 int modelBitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900398
399 /*
400 Here we try to be robust against wrong size fields with the goal of having PiCriterion independent of the
401 pipeline model. We duplicate the field match, fitting the byte sequences to the bit-width specified in the
402 model. This operation is expensive when performed for each field match of each flow rule, but should be
403 mitigated by the translation cache provided by PiFlowRuleTranslationServiceImpl.
404 */
405
406 try {
407 switch (fieldModel.matchType()) {
408 case EXACT:
409 return new PiExactFieldMatch(fieldMatch.fieldId(),
410 fit(((PiExactFieldMatch) fieldMatch).value(), modelBitWidth));
411 case TERNARY:
412 return new PiTernaryFieldMatch(fieldMatch.fieldId(),
413 fit(((PiTernaryFieldMatch) fieldMatch).value(), modelBitWidth),
414 fit(((PiTernaryFieldMatch) fieldMatch).mask(), modelBitWidth));
415 case LPM:
416 PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
417 if (lpmfield.prefixLength() > modelBitWidth) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200418 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900419 "Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
420 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
421 }
422 return new PiLpmFieldMatch(fieldMatch.fieldId(),
423 fit(lpmfield.value(), modelBitWidth),
424 lpmfield.prefixLength());
425 case RANGE:
426 return new PiRangeFieldMatch(fieldMatch.fieldId(),
427 fit(((PiRangeFieldMatch) fieldMatch).lowValue(), modelBitWidth),
428 fit(((PiRangeFieldMatch) fieldMatch).highValue(), modelBitWidth));
429 case VALID:
430 return fieldMatch;
431 default:
432 // Should never be here.
433 throw new RuntimeException(
434 "Unrecognized match type " + fieldModel.matchType().name());
435 }
436 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200437 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900438 "Size mismatch for field %s: %s", fieldMatch.fieldId(), e.getMessage()));
439 }
440 }
441}