blob: c637144a264605b28c1be6b094b2c252861bc820 [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.
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700100 final PiMatchKey piMatchKey;
101 final boolean needPriority;
102 if (rule.selector().criteria().isEmpty()) {
103 piMatchKey = PiMatchKey.EMPTY;
104 needPriority = false;
105 } else {
106 final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(
107 interpreter, rule.selector(), tableModel);
108 piMatchKey = PiMatchKey.builder()
109 .addFieldMatches(fieldMatches)
110 .build();
111 // FIXME: P4Runtime limit
112 // Need to ignore priority if no TCAM lookup match field
113 needPriority = fieldMatches.stream()
114 .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
115 match.type() == PiMatchType.RANGE);
116 }
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200117 // Translate treatment.
118 final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
Carmelo Casconea62ac3d2017-08-30 03:19:00 +0200119
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200120 // Build PI entry.
121 final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900122
Carmelo Cascone00a59962017-06-16 17:51:49 +0900123 tableEntryBuilder
124 .forTable(piTableId)
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700125 .withMatchKey(piMatchKey)
Yi Tseng82512da2017-08-16 19:46:36 -0700126 .withAction(piTableAction);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900127
Carmelo Casconef6c2f052018-03-23 18:02:15 -0700128 if (needPriority) {
Yi Tseng02c4c572018-01-22 17:52:10 -0800129 tableEntryBuilder.withPriority(rule.priority());
130 }
131
Carmelo Cascone00a59962017-06-16 17:51:49 +0900132 if (!rule.isPermanent()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200133 if (tableModel.supportsAging()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900134 tableEntryBuilder.withTimeout((double) rule.timeout());
135 } else {
136 log.warn("Flow rule is temporary, but table '{}' doesn't support " +
Carmelo Cascone87892e22017-11-13 16:01:29 -0800137 "aging, translating to permanent.", tableModel.id());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900138 }
139
140 }
141
142 return tableEntryBuilder.build();
143 }
144
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200145
146 /**
147 * Returns a PI action equivalent to the given treatment, optionally using the given interpreter. This method also
148 * checks that the produced PI table action is suitable for the given table ID and pipeline model. If suitable, the
149 * returned action instance will have parameters well-sized, according to the table model.
150 *
151 * @param treatment traffic treatment
152 * @param interpreter interpreter
153 * @param tableId PI table ID
154 * @param pipelineModel pipeline model
155 * @return PI table action
156 * @throws PiTranslationException if the treatment cannot be translated or if the PI action is not suitable for the
157 * given pipeline model
158 */
159 static PiTableAction translateTreatment(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
160 PiTableId tableId, PiPipelineModel pipelineModel)
161 throws PiTranslationException {
162 PiTableModel tableModel = getTableModel(tableId, pipelineModel);
163 return typeCheckAction(buildAction(treatment, interpreter, tableId), tableModel);
164 }
165
166 private static PiTableModel getTableModel(PiTableId piTableId, PiPipelineModel pipelineModel)
167 throws PiTranslationException {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800168 return pipelineModel.table(piTableId)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200169 .orElseThrow(() -> new PiTranslationException(format(
170 "Not such a table in pipeline model: %s", piTableId)));
171 }
172
Carmelo Cascone00a59962017-06-16 17:51:49 +0900173 /**
174 * Builds a PI action out of the given treatment, optionally using the given interpreter.
175 */
Yi Tseng82512da2017-08-16 19:46:36 -0700176 private static PiTableAction buildAction(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200177 PiTableId tableId)
178 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900179
180 PiTableAction piTableAction = null;
181
182 // If treatment has only one instruction of type PiInstruction, use that.
183 for (Instruction inst : treatment.allInstructions()) {
184 if (inst.type() == Instruction.Type.PROTOCOL_INDEPENDENT) {
185 if (treatment.allInstructions().size() == 1) {
186 piTableAction = ((PiInstruction) inst).action();
187 } else {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200188 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900189 "Unable to translate treatment, found multiple instructions " +
190 "of which one is protocol-independent: %s", treatment));
191 }
192 }
193 }
194
195 if (piTableAction == null && interpreter != null) {
196 // No PiInstruction, use interpreter to build action.
197 try {
Carmelo Casconef3a1a382017-07-27 12:04:39 -0400198 piTableAction = interpreter.mapTreatment(treatment, tableId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900199 } catch (PiPipelineInterpreter.PiInterpreterException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200200 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900201 "Interpreter was unable to translate treatment. " + e.getMessage());
202 }
203 }
204
205 if (piTableAction == null) {
206 // No PiInstruction, no interpreter. It's time to give up.
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200207 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900208 "Unable to translate treatment, neither an interpreter or a "
209 + "protocol-independent instruction were provided.");
210 }
211
Yi Tseng82512da2017-08-16 19:46:36 -0700212 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900213 }
214
Yi Tseng82512da2017-08-16 19:46:36 -0700215 private static PiTableAction typeCheckAction(PiTableAction piTableAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200216 throws PiTranslationException {
Yi Tseng82512da2017-08-16 19:46:36 -0700217 switch (piTableAction.type()) {
218 case ACTION:
219 return checkPiAction((PiAction) piTableAction, table);
220 default:
221 // FIXME: should we check? how?
222 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900223
Yi Tseng82512da2017-08-16 19:46:36 -0700224 }
225 }
226
227 private static PiTableAction checkPiAction(PiAction piAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200228 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900229 // Table supports this action?
Carmelo Cascone87892e22017-11-13 16:01:29 -0800230 PiActionModel actionModel = table.action(piAction.id()).orElseThrow(
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200231 () -> new PiTranslationException(format("Not such action '%s' for table '%s'",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800232 piAction.id(), table.id())));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900233
234 // Is the number of runtime parameters correct?
235 if (actionModel.params().size() != piAction.parameters().size()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200236 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900237 "Wrong number of runtime parameters for action '%s', expected %d but found %d",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800238 actionModel.id(), actionModel.params().size(), piAction.parameters().size()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900239 }
240
241 // Forge a new action instance with well-sized parameters.
242 // The same comment as in typeCheckFieldMatch() about duplicating field match instances applies here.
243 PiAction.Builder newActionBuilder = PiAction.builder().withId(piAction.id());
244 for (PiActionParam param : piAction.parameters()) {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800245 PiActionParamModel paramModel = actionModel.param(param.id())
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200246 .orElseThrow(() -> new PiTranslationException(format(
Carmelo Cascone87892e22017-11-13 16:01:29 -0800247 "Not such parameter '%s' for action '%s'", param.id(), actionModel)));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900248 try {
249 newActionBuilder.withParameter(new PiActionParam(param.id(),
250 fit(param.value(), paramModel.bitWidth())));
251 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200252 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900253 "Size mismatch for parameter '%s' of action '%s': %s",
254 param.id(), piAction.id(), e.getMessage()));
255 }
256 }
257
258 return newActionBuilder.build();
259 }
260
261 /**
262 * Builds a collection of PI field matches out of the given selector, optionally using the given interpreter. The
263 * field matches returned are guaranteed to be compatible for the given table model.
264 */
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200265 private static Collection<PiFieldMatch> translateFieldMatches(PiPipelineInterpreter interpreter,
266 TrafficSelector selector, PiTableModel tableModel)
267 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900268
Carmelo Cascone87892e22017-11-13 16:01:29 -0800269 Map<PiMatchFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900270
271 // 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 -0800272 Map<PiMatchFieldId, PiFieldMatch> piCriterionFields = selector.criteria().stream()
Carmelo Cascone00a59962017-06-16 17:51:49 +0900273 .filter(c -> c.type().equals(PROTOCOL_INDEPENDENT))
274 .map(c -> (PiCriterion) c)
275 .findFirst()
276 .map(PiCriterion::fieldMatches)
277 .map(c -> {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800278 Map<PiMatchFieldId, PiFieldMatch> fieldMap = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900279 c.forEach(fieldMatch -> fieldMap.put(fieldMatch.fieldId(), fieldMatch));
280 return fieldMap;
281 })
282 .orElse(Maps.newHashMap());
283
284 Set<Criterion> translatedCriteria = Sets.newHashSet();
285 Set<Criterion> ignoredCriteria = Sets.newHashSet();
Carmelo Cascone87892e22017-11-13 16:01:29 -0800286 Set<PiMatchFieldId> usedPiCriterionFields = Sets.newHashSet();
287 Set<PiMatchFieldId> ignoredPiCriterionFields = Sets.newHashSet();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900288
Carmelo Cascone87892e22017-11-13 16:01:29 -0800289 for (PiMatchFieldModel fieldModel : tableModel.matchFields()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900290
Carmelo Cascone87892e22017-11-13 16:01:29 -0800291 PiMatchFieldId fieldId = fieldModel.id();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900292
Carmelo Cascone87892e22017-11-13 16:01:29 -0800293 int bitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900294 int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);
295
296 Optional<Criterion.Type> criterionType =
297 interpreter == null
298 ? Optional.empty()
Carmelo Cascone87892e22017-11-13 16:01:29 -0800299 : interpreter.mapPiMatchFieldId(fieldId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900300
301 Criterion criterion = criterionType.map(selector::getCriterion).orElse(null);
302
303 if (!piCriterionFields.containsKey(fieldId) && criterion == null) {
304 // Neither a field in PiCriterion is available nor a Criterion mapping is possible.
305 // Can ignore if the match is ternary or LPM.
306 switch (fieldModel.matchType()) {
307 case TERNARY:
308 // Wildcard the whole field.
309 fieldMatches.put(fieldId, new PiTernaryFieldMatch(
310 fieldId,
311 ImmutableByteSequence.ofZeros(fieldByteWidth),
312 ImmutableByteSequence.ofZeros(fieldByteWidth)));
313 break;
314 case LPM:
315 // LPM with prefix 0
316 fieldMatches.put(fieldId, new PiLpmFieldMatch(fieldId,
317 ImmutableByteSequence.ofZeros(fieldByteWidth),
318 0));
319 break;
320 // FIXME: Can we handle the case of RANGE or VALID match?
321 default:
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200322 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900323 "No value found for required match field '%s'", fieldId));
324 }
325 // Next field.
326 continue;
327 }
328
329 PiFieldMatch fieldMatch = null;
330
331 if (criterion != null) {
332 // Criterion mapping is possible for this field id.
333 try {
334 fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
335 translatedCriteria.add(criterion);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200336 } catch (PiTranslationException ex) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900337 // Ignore exception if the same field was found in PiCriterion.
338 if (piCriterionFields.containsKey(fieldId)) {
339 ignoredCriteria.add(criterion);
340 } else {
341 throw ex;
342 }
343 }
344 }
345
346 if (piCriterionFields.containsKey(fieldId)) {
347 // Field was found in PiCriterion.
348 if (fieldMatch != null) {
349 // Field was already translated from other criterion.
350 // Throw exception only if we are trying to match on different values of the same field...
351 if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200352 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900353 "Duplicate match field '%s': instance translated from criterion '%s' is different to " +
354 "what found in PiCriterion.", fieldId, criterion.type()));
355 }
356 ignoredPiCriterionFields.add(fieldId);
357 } else {
358 fieldMatch = piCriterionFields.get(fieldId);
359 fieldMatch = typeCheckFieldMatch(fieldMatch, fieldModel);
360 usedPiCriterionFields.add(fieldId);
361 }
362 }
363
364 fieldMatches.put(fieldId, fieldMatch);
365 }
366
367 // Check if all criteria have been translated.
368 StringJoiner skippedCriteriaJoiner = new StringJoiner(", ");
369 selector.criteria().stream()
370 .filter(c -> !c.type().equals(PROTOCOL_INDEPENDENT))
371 .filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c))
372 .forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
373 if (skippedCriteriaJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200374 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900375 "The following criteria cannot be translated for table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800376 tableModel.id(), skippedCriteriaJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900377 }
378
379 // Check if all fields found in PiCriterion have been used.
380 StringJoiner skippedPiFieldsJoiner = new StringJoiner(", ");
381 piCriterionFields.keySet().stream()
382 .filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k))
383 .forEach(k -> skippedPiFieldsJoiner.add(k.id()));
384 if (skippedPiFieldsJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200385 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900386 "The following PiCriterion field matches are not supported in table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800387 tableModel.id(), skippedPiFieldsJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900388 }
389
390 return fieldMatches.values();
391 }
392
Carmelo Cascone87892e22017-11-13 16:01:29 -0800393 private static PiFieldMatch typeCheckFieldMatch(PiFieldMatch fieldMatch, PiMatchFieldModel fieldModel)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200394 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900395
396 // Check parameter type and size
397 if (!fieldModel.matchType().equals(fieldMatch.type())) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200398 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900399 "Wrong match type for field '%s', expected %s, but found %s",
400 fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
401 }
402
Carmelo Cascone87892e22017-11-13 16:01:29 -0800403 int modelBitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900404
405 /*
406 Here we try to be robust against wrong size fields with the goal of having PiCriterion independent of the
407 pipeline model. We duplicate the field match, fitting the byte sequences to the bit-width specified in the
408 model. This operation is expensive when performed for each field match of each flow rule, but should be
409 mitigated by the translation cache provided by PiFlowRuleTranslationServiceImpl.
410 */
411
412 try {
413 switch (fieldModel.matchType()) {
414 case EXACT:
415 return new PiExactFieldMatch(fieldMatch.fieldId(),
416 fit(((PiExactFieldMatch) fieldMatch).value(), modelBitWidth));
417 case TERNARY:
418 return new PiTernaryFieldMatch(fieldMatch.fieldId(),
419 fit(((PiTernaryFieldMatch) fieldMatch).value(), modelBitWidth),
420 fit(((PiTernaryFieldMatch) fieldMatch).mask(), modelBitWidth));
421 case LPM:
422 PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
423 if (lpmfield.prefixLength() > modelBitWidth) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200424 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900425 "Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
426 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
427 }
428 return new PiLpmFieldMatch(fieldMatch.fieldId(),
429 fit(lpmfield.value(), modelBitWidth),
430 lpmfield.prefixLength());
431 case RANGE:
432 return new PiRangeFieldMatch(fieldMatch.fieldId(),
433 fit(((PiRangeFieldMatch) fieldMatch).lowValue(), modelBitWidth),
434 fit(((PiRangeFieldMatch) fieldMatch).highValue(), modelBitWidth));
435 case VALID:
436 return fieldMatch;
437 default:
438 // Should never be here.
Ray Milkey986a47a2018-01-25 11:38:51 -0800439 throw new IllegalArgumentException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900440 "Unrecognized match type " + fieldModel.matchType().name());
441 }
442 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200443 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900444 "Size mismatch for field %s: %s", fieldMatch.fieldId(), e.getMessage()));
445 }
446 }
447}