blob: 3230f4157e2b84dc28d26730f31c7c83f5dcfe40 [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;
Yi Tseng02c4c572018-01-22 17:52:10 -080034import org.onosproject.net.pi.model.PiMatchType;
Carmelo Cascone00a59962017-06-16 17:51:49 +090035import org.onosproject.net.pi.model.PiPipeconf;
36import org.onosproject.net.pi.model.PiPipelineInterpreter;
37import org.onosproject.net.pi.model.PiPipelineModel;
Carmelo Cascone87892e22017-11-13 16:01:29 -080038import org.onosproject.net.pi.model.PiTableId;
Carmelo Cascone00a59962017-06-16 17:51:49 +090039import org.onosproject.net.pi.model.PiTableModel;
40import org.onosproject.net.pi.runtime.PiAction;
41import org.onosproject.net.pi.runtime.PiActionParam;
42import org.onosproject.net.pi.runtime.PiExactFieldMatch;
43import org.onosproject.net.pi.runtime.PiFieldMatch;
Carmelo Cascone00a59962017-06-16 17:51:49 +090044import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
Carmelo Cascone0e896a02017-07-31 07:22:27 +020045import org.onosproject.net.pi.runtime.PiMatchKey;
Carmelo Cascone00a59962017-06-16 17:51:49 +090046import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
47import org.onosproject.net.pi.runtime.PiTableAction;
48import org.onosproject.net.pi.runtime.PiTableEntry;
Carmelo Cascone00a59962017-06-16 17:51:49 +090049import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080050import org.onosproject.net.pi.service.PiTranslationException;
Carmelo Cascone00a59962017-06-16 17:51:49 +090051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54import java.util.Collection;
55import java.util.Map;
56import java.util.Optional;
57import java.util.Set;
58import java.util.StringJoiner;
59
60import static java.lang.String.format;
61import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
62import static org.onlab.util.ImmutableByteSequence.fit;
63import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
64import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020065import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
66import static org.onosproject.net.pi.impl.PiUtils.translateTableId;
Carmelo Cascone00a59962017-06-16 17:51:49 +090067
68/**
69 * Implementation of flow rule translation logic.
70 */
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080071final class PiFlowRuleTranslatorImpl {
Carmelo Cascone00a59962017-06-16 17:51:49 +090072
Carmelo Casconea3d811c2017-08-22 01:03:45 +020073 public static final int MAX_PI_PRIORITY = (int) Math.pow(2, 24);
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080074 private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslatorImpl.class);
Carmelo Cascone00a59962017-06-16 17:51:49 +090075
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080076 private PiFlowRuleTranslatorImpl() {
Carmelo Cascone00a59962017-06-16 17:51:49 +090077 // Hide constructor.
78 }
79
Carmelo Cascone87b9b392017-10-02 18:33:20 +020080 /**
81 * Returns a PI table entry equivalent to the given flow rule, for the given pipeconf and device.
82 *
83 * @param rule flow rule
84 * @param pipeconf pipeconf
85 * @param device device
86 * @return PI table entry
87 * @throws PiTranslationException if the flow rule cannot be translated
88 */
89 static PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf, Device device)
90 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +090091
92 PiPipelineModel pipelineModel = pipeconf.pipelineModel();
93
94 // Retrieve interpreter, if any.
Carmelo Cascone87b9b392017-10-02 18:33:20 +020095 final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
96 // Get table model.
97 final PiTableId piTableId = translateTableId(rule.table(), interpreter);
98 final PiTableModel tableModel = getTableModel(piTableId, pipelineModel);
99 // Translate selector.
100 final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(interpreter, rule.selector(), tableModel);
101 // Translate treatment.
102 final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
Carmelo Casconea62ac3d2017-08-30 03:19:00 +0200103
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200104 // Build PI entry.
105 final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900106
Yi Tseng02c4c572018-01-22 17:52:10 -0800107 // FIXME: P4Runtime limit
108 // Need to ignore priority if no TCAM lookup match field
109 boolean dontIgnorePriority = fieldMatches.stream()
110 .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
111 match.type() == PiMatchType.RANGE);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900112
113 tableEntryBuilder
114 .forTable(piTableId)
Carmelo Cascone0e896a02017-07-31 07:22:27 +0200115 .withMatchKey(PiMatchKey.builder()
116 .addFieldMatches(fieldMatches)
117 .build())
Yi Tseng82512da2017-08-16 19:46:36 -0700118 .withAction(piTableAction);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900119
Yi Tseng02c4c572018-01-22 17:52:10 -0800120 if (dontIgnorePriority) {
121 tableEntryBuilder.withPriority(rule.priority());
122 }
123
Carmelo Cascone00a59962017-06-16 17:51:49 +0900124 if (!rule.isPermanent()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200125 if (tableModel.supportsAging()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900126 tableEntryBuilder.withTimeout((double) rule.timeout());
127 } else {
128 log.warn("Flow rule is temporary, but table '{}' doesn't support " +
Carmelo Cascone87892e22017-11-13 16:01:29 -0800129 "aging, translating to permanent.", tableModel.id());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900130 }
131
132 }
133
134 return tableEntryBuilder.build();
135 }
136
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200137
138 /**
139 * Returns a PI action equivalent to the given treatment, optionally using the given interpreter. This method also
140 * checks that the produced PI table action is suitable for the given table ID and pipeline model. If suitable, the
141 * returned action instance will have parameters well-sized, according to the table model.
142 *
143 * @param treatment traffic treatment
144 * @param interpreter interpreter
145 * @param tableId PI table ID
146 * @param pipelineModel pipeline model
147 * @return PI table action
148 * @throws PiTranslationException if the treatment cannot be translated or if the PI action is not suitable for the
149 * given pipeline model
150 */
151 static PiTableAction translateTreatment(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
152 PiTableId tableId, PiPipelineModel pipelineModel)
153 throws PiTranslationException {
154 PiTableModel tableModel = getTableModel(tableId, pipelineModel);
155 return typeCheckAction(buildAction(treatment, interpreter, tableId), tableModel);
156 }
157
158 private static PiTableModel getTableModel(PiTableId piTableId, PiPipelineModel pipelineModel)
159 throws PiTranslationException {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800160 return pipelineModel.table(piTableId)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200161 .orElseThrow(() -> new PiTranslationException(format(
162 "Not such a table in pipeline model: %s", piTableId)));
163 }
164
Carmelo Cascone00a59962017-06-16 17:51:49 +0900165 /**
166 * Builds a PI action out of the given treatment, optionally using the given interpreter.
167 */
Yi Tseng82512da2017-08-16 19:46:36 -0700168 private static PiTableAction buildAction(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200169 PiTableId tableId)
170 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900171
172 PiTableAction piTableAction = null;
173
174 // If treatment has only one instruction of type PiInstruction, use that.
175 for (Instruction inst : treatment.allInstructions()) {
176 if (inst.type() == Instruction.Type.PROTOCOL_INDEPENDENT) {
177 if (treatment.allInstructions().size() == 1) {
178 piTableAction = ((PiInstruction) inst).action();
179 } else {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200180 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900181 "Unable to translate treatment, found multiple instructions " +
182 "of which one is protocol-independent: %s", treatment));
183 }
184 }
185 }
186
187 if (piTableAction == null && interpreter != null) {
188 // No PiInstruction, use interpreter to build action.
189 try {
Carmelo Casconef3a1a382017-07-27 12:04:39 -0400190 piTableAction = interpreter.mapTreatment(treatment, tableId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900191 } catch (PiPipelineInterpreter.PiInterpreterException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200192 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900193 "Interpreter was unable to translate treatment. " + e.getMessage());
194 }
195 }
196
197 if (piTableAction == null) {
198 // No PiInstruction, no interpreter. It's time to give up.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200199 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900200 "Unable to translate treatment, neither an interpreter or a "
201 + "protocol-independent instruction were provided.");
202 }
203
Yi Tseng82512da2017-08-16 19:46:36 -0700204 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900205 }
206
Yi Tseng82512da2017-08-16 19:46:36 -0700207 private static PiTableAction typeCheckAction(PiTableAction piTableAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200208 throws PiTranslationException {
Yi Tseng82512da2017-08-16 19:46:36 -0700209 switch (piTableAction.type()) {
210 case ACTION:
211 return checkPiAction((PiAction) piTableAction, table);
212 default:
213 // FIXME: should we check? how?
214 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900215
Yi Tseng82512da2017-08-16 19:46:36 -0700216 }
217 }
218
219 private static PiTableAction checkPiAction(PiAction piAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200220 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900221 // Table supports this action?
Carmelo Cascone87892e22017-11-13 16:01:29 -0800222 PiActionModel actionModel = table.action(piAction.id()).orElseThrow(
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200223 () -> new PiTranslationException(format("Not such action '%s' for table '%s'",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800224 piAction.id(), table.id())));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900225
226 // Is the number of runtime parameters correct?
227 if (actionModel.params().size() != piAction.parameters().size()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200228 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900229 "Wrong number of runtime parameters for action '%s', expected %d but found %d",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800230 actionModel.id(), actionModel.params().size(), piAction.parameters().size()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900231 }
232
233 // Forge a new action instance with well-sized parameters.
234 // The same comment as in typeCheckFieldMatch() about duplicating field match instances applies here.
235 PiAction.Builder newActionBuilder = PiAction.builder().withId(piAction.id());
236 for (PiActionParam param : piAction.parameters()) {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800237 PiActionParamModel paramModel = actionModel.param(param.id())
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200238 .orElseThrow(() -> new PiTranslationException(format(
Carmelo Cascone87892e22017-11-13 16:01:29 -0800239 "Not such parameter '%s' for action '%s'", param.id(), actionModel)));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900240 try {
241 newActionBuilder.withParameter(new PiActionParam(param.id(),
242 fit(param.value(), paramModel.bitWidth())));
243 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200244 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900245 "Size mismatch for parameter '%s' of action '%s': %s",
246 param.id(), piAction.id(), e.getMessage()));
247 }
248 }
249
250 return newActionBuilder.build();
251 }
252
253 /**
254 * Builds a collection of PI field matches out of the given selector, optionally using the given interpreter. The
255 * field matches returned are guaranteed to be compatible for the given table model.
256 */
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200257 private static Collection<PiFieldMatch> translateFieldMatches(PiPipelineInterpreter interpreter,
258 TrafficSelector selector, PiTableModel tableModel)
259 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900260
Carmelo Cascone87892e22017-11-13 16:01:29 -0800261 Map<PiMatchFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900262
263 // 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 -0800264 Map<PiMatchFieldId, PiFieldMatch> piCriterionFields = selector.criteria().stream()
Carmelo Cascone00a59962017-06-16 17:51:49 +0900265 .filter(c -> c.type().equals(PROTOCOL_INDEPENDENT))
266 .map(c -> (PiCriterion) c)
267 .findFirst()
268 .map(PiCriterion::fieldMatches)
269 .map(c -> {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800270 Map<PiMatchFieldId, PiFieldMatch> fieldMap = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900271 c.forEach(fieldMatch -> fieldMap.put(fieldMatch.fieldId(), fieldMatch));
272 return fieldMap;
273 })
274 .orElse(Maps.newHashMap());
275
276 Set<Criterion> translatedCriteria = Sets.newHashSet();
277 Set<Criterion> ignoredCriteria = Sets.newHashSet();
Carmelo Cascone87892e22017-11-13 16:01:29 -0800278 Set<PiMatchFieldId> usedPiCriterionFields = Sets.newHashSet();
279 Set<PiMatchFieldId> ignoredPiCriterionFields = Sets.newHashSet();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900280
Carmelo Cascone87892e22017-11-13 16:01:29 -0800281 for (PiMatchFieldModel fieldModel : tableModel.matchFields()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900282
Carmelo Cascone87892e22017-11-13 16:01:29 -0800283 PiMatchFieldId fieldId = fieldModel.id();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900284
Carmelo Cascone87892e22017-11-13 16:01:29 -0800285 int bitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900286 int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);
287
288 Optional<Criterion.Type> criterionType =
289 interpreter == null
290 ? Optional.empty()
Carmelo Cascone87892e22017-11-13 16:01:29 -0800291 : interpreter.mapPiMatchFieldId(fieldId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900292
293 Criterion criterion = criterionType.map(selector::getCriterion).orElse(null);
294
295 if (!piCriterionFields.containsKey(fieldId) && criterion == null) {
296 // Neither a field in PiCriterion is available nor a Criterion mapping is possible.
297 // Can ignore if the match is ternary or LPM.
298 switch (fieldModel.matchType()) {
299 case TERNARY:
300 // Wildcard the whole field.
301 fieldMatches.put(fieldId, new PiTernaryFieldMatch(
302 fieldId,
303 ImmutableByteSequence.ofZeros(fieldByteWidth),
304 ImmutableByteSequence.ofZeros(fieldByteWidth)));
305 break;
306 case LPM:
307 // LPM with prefix 0
308 fieldMatches.put(fieldId, new PiLpmFieldMatch(fieldId,
309 ImmutableByteSequence.ofZeros(fieldByteWidth),
310 0));
311 break;
312 // FIXME: Can we handle the case of RANGE or VALID match?
313 default:
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200314 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900315 "No value found for required match field '%s'", fieldId));
316 }
317 // Next field.
318 continue;
319 }
320
321 PiFieldMatch fieldMatch = null;
322
323 if (criterion != null) {
324 // Criterion mapping is possible for this field id.
325 try {
326 fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
327 translatedCriteria.add(criterion);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200328 } catch (PiTranslationException ex) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900329 // Ignore exception if the same field was found in PiCriterion.
330 if (piCriterionFields.containsKey(fieldId)) {
331 ignoredCriteria.add(criterion);
332 } else {
333 throw ex;
334 }
335 }
336 }
337
338 if (piCriterionFields.containsKey(fieldId)) {
339 // Field was found in PiCriterion.
340 if (fieldMatch != null) {
341 // Field was already translated from other criterion.
342 // Throw exception only if we are trying to match on different values of the same field...
343 if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200344 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900345 "Duplicate match field '%s': instance translated from criterion '%s' is different to " +
346 "what found in PiCriterion.", fieldId, criterion.type()));
347 }
348 ignoredPiCriterionFields.add(fieldId);
349 } else {
350 fieldMatch = piCriterionFields.get(fieldId);
351 fieldMatch = typeCheckFieldMatch(fieldMatch, fieldModel);
352 usedPiCriterionFields.add(fieldId);
353 }
354 }
355
356 fieldMatches.put(fieldId, fieldMatch);
357 }
358
359 // Check if all criteria have been translated.
360 StringJoiner skippedCriteriaJoiner = new StringJoiner(", ");
361 selector.criteria().stream()
362 .filter(c -> !c.type().equals(PROTOCOL_INDEPENDENT))
363 .filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c))
364 .forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
365 if (skippedCriteriaJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200366 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900367 "The following criteria cannot be translated for table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800368 tableModel.id(), skippedCriteriaJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900369 }
370
371 // Check if all fields found in PiCriterion have been used.
372 StringJoiner skippedPiFieldsJoiner = new StringJoiner(", ");
373 piCriterionFields.keySet().stream()
374 .filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k))
375 .forEach(k -> skippedPiFieldsJoiner.add(k.id()));
376 if (skippedPiFieldsJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200377 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900378 "The following PiCriterion field matches are not supported in table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800379 tableModel.id(), skippedPiFieldsJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900380 }
381
382 return fieldMatches.values();
383 }
384
Carmelo Cascone87892e22017-11-13 16:01:29 -0800385 private static PiFieldMatch typeCheckFieldMatch(PiFieldMatch fieldMatch, PiMatchFieldModel fieldModel)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200386 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900387
388 // Check parameter type and size
389 if (!fieldModel.matchType().equals(fieldMatch.type())) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200390 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900391 "Wrong match type for field '%s', expected %s, but found %s",
392 fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
393 }
394
Carmelo Cascone87892e22017-11-13 16:01:29 -0800395 int modelBitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900396
397 /*
398 Here we try to be robust against wrong size fields with the goal of having PiCriterion independent of the
399 pipeline model. We duplicate the field match, fitting the byte sequences to the bit-width specified in the
400 model. This operation is expensive when performed for each field match of each flow rule, but should be
401 mitigated by the translation cache provided by PiFlowRuleTranslationServiceImpl.
402 */
403
404 try {
405 switch (fieldModel.matchType()) {
406 case EXACT:
407 return new PiExactFieldMatch(fieldMatch.fieldId(),
408 fit(((PiExactFieldMatch) fieldMatch).value(), modelBitWidth));
409 case TERNARY:
410 return new PiTernaryFieldMatch(fieldMatch.fieldId(),
411 fit(((PiTernaryFieldMatch) fieldMatch).value(), modelBitWidth),
412 fit(((PiTernaryFieldMatch) fieldMatch).mask(), modelBitWidth));
413 case LPM:
414 PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
415 if (lpmfield.prefixLength() > modelBitWidth) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200416 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900417 "Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
418 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
419 }
420 return new PiLpmFieldMatch(fieldMatch.fieldId(),
421 fit(lpmfield.value(), modelBitWidth),
422 lpmfield.prefixLength());
423 case RANGE:
424 return new PiRangeFieldMatch(fieldMatch.fieldId(),
425 fit(((PiRangeFieldMatch) fieldMatch).lowValue(), modelBitWidth),
426 fit(((PiRangeFieldMatch) fieldMatch).highValue(), modelBitWidth));
427 case VALID:
428 return fieldMatch;
429 default:
430 // Should never be here.
Ray Milkey986a47a2018-01-25 11:38:51 -0800431 throw new IllegalArgumentException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900432 "Unrecognized match type " + fieldModel.matchType().name());
433 }
434 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200435 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900436 "Size mismatch for field %s: %s", fieldMatch.fieldId(), e.getMessage()));
437 }
438 }
439}