blob: 85c763caa2e5a63d12518e8283acba289a101b6e [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;
Carmelo Cascone00a59962017-06-16 17:51:49 +090062import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
63import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
Carmelo Cascone87b9b392017-10-02 18:33:20 +020064import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
65import static org.onosproject.net.pi.impl.PiUtils.translateTableId;
Carmelo Cascone00a59962017-06-16 17:51:49 +090066
67/**
68 * Implementation of flow rule translation logic.
69 */
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080070final class PiFlowRuleTranslatorImpl {
Carmelo Cascone00a59962017-06-16 17:51:49 +090071
Carmelo Casconea3d811c2017-08-22 01:03:45 +020072 public static final int MAX_PI_PRIORITY = (int) Math.pow(2, 24);
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080073 private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslatorImpl.class);
Carmelo Cascone00a59962017-06-16 17:51:49 +090074
Carmelo Cascone326ad2d2017-11-28 18:09:13 -080075 private PiFlowRuleTranslatorImpl() {
Carmelo Cascone00a59962017-06-16 17:51:49 +090076 // 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.
Carmelo Cascone4256bde2018-03-23 18:02:15 -070099 final PiMatchKey piMatchKey;
100 final boolean needPriority;
101 if (rule.selector().criteria().isEmpty()) {
102 piMatchKey = PiMatchKey.EMPTY;
103 needPriority = false;
104 } else {
105 final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(
106 interpreter, rule.selector(), tableModel);
107 piMatchKey = PiMatchKey.builder()
108 .addFieldMatches(fieldMatches)
109 .build();
110 // FIXME: P4Runtime limit
111 // Need to ignore priority if no TCAM lookup match field
112 needPriority = fieldMatches.stream()
113 .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
114 match.type() == PiMatchType.RANGE);
115 }
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200116 // Translate treatment.
117 final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
Carmelo Casconea62ac3d2017-08-30 03:19:00 +0200118
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200119 // Build PI entry.
120 final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900121
Carmelo Cascone00a59962017-06-16 17:51:49 +0900122 tableEntryBuilder
123 .forTable(piTableId)
Yi Tsengdf3eec52018-02-15 14:56:02 -0800124 .withMatchKey(piMatchKey);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900125
Yi Tsengd28936e2018-02-23 22:11:11 +0100126 if (piTableAction != null) {
127 tableEntryBuilder.withAction(piTableAction);
128 }
129
Carmelo Cascone4256bde2018-03-23 18:02:15 -0700130 if (needPriority) {
Yi Tseng02c4c572018-01-22 17:52:10 -0800131 tableEntryBuilder.withPriority(rule.priority());
132 }
133
Carmelo Cascone00a59962017-06-16 17:51:49 +0900134 if (!rule.isPermanent()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200135 if (tableModel.supportsAging()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900136 tableEntryBuilder.withTimeout((double) rule.timeout());
137 } else {
138 log.warn("Flow rule is temporary, but table '{}' doesn't support " +
Carmelo Cascone87892e22017-11-13 16:01:29 -0800139 "aging, translating to permanent.", tableModel.id());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900140 }
141
142 }
143
144 return tableEntryBuilder.build();
145 }
146
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200147
148 /**
149 * Returns a PI action equivalent to the given treatment, optionally using the given interpreter. This method also
150 * checks that the produced PI table action is suitable for the given table ID and pipeline model. If suitable, the
151 * returned action instance will have parameters well-sized, according to the table model.
152 *
153 * @param treatment traffic treatment
154 * @param interpreter interpreter
155 * @param tableId PI table ID
156 * @param pipelineModel pipeline model
157 * @return PI table action
158 * @throws PiTranslationException if the treatment cannot be translated or if the PI action is not suitable for the
159 * given pipeline model
160 */
161 static PiTableAction translateTreatment(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
162 PiTableId tableId, PiPipelineModel pipelineModel)
163 throws PiTranslationException {
164 PiTableModel tableModel = getTableModel(tableId, pipelineModel);
165 return typeCheckAction(buildAction(treatment, interpreter, tableId), tableModel);
166 }
167
168 private static PiTableModel getTableModel(PiTableId piTableId, PiPipelineModel pipelineModel)
169 throws PiTranslationException {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800170 return pipelineModel.table(piTableId)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200171 .orElseThrow(() -> new PiTranslationException(format(
172 "Not such a table in pipeline model: %s", piTableId)));
173 }
174
Carmelo Cascone00a59962017-06-16 17:51:49 +0900175 /**
176 * Builds a PI action out of the given treatment, optionally using the given interpreter.
177 */
Yi Tseng82512da2017-08-16 19:46:36 -0700178 private static PiTableAction buildAction(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200179 PiTableId tableId)
180 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900181
182 PiTableAction piTableAction = null;
183
184 // If treatment has only one instruction of type PiInstruction, use that.
185 for (Instruction inst : treatment.allInstructions()) {
186 if (inst.type() == Instruction.Type.PROTOCOL_INDEPENDENT) {
187 if (treatment.allInstructions().size() == 1) {
188 piTableAction = ((PiInstruction) inst).action();
189 } else {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200190 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900191 "Unable to translate treatment, found multiple instructions " +
192 "of which one is protocol-independent: %s", treatment));
193 }
194 }
195 }
196
197 if (piTableAction == null && interpreter != null) {
198 // No PiInstruction, use interpreter to build action.
199 try {
Carmelo Casconef3a1a382017-07-27 12:04:39 -0400200 piTableAction = interpreter.mapTreatment(treatment, tableId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900201 } catch (PiPipelineInterpreter.PiInterpreterException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200202 throw new PiTranslationException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900203 "Interpreter was unable to translate treatment. " + e.getMessage());
204 }
205 }
206
Yi Tseng82512da2017-08-16 19:46:36 -0700207 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900208 }
209
Yi Tseng82512da2017-08-16 19:46:36 -0700210 private static PiTableAction typeCheckAction(PiTableAction piTableAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200211 throws PiTranslationException {
Yi Tsengd28936e2018-02-23 22:11:11 +0100212 if (piTableAction == null) {
213 // skip check if null
214 return null;
215 }
Yi Tseng82512da2017-08-16 19:46:36 -0700216 switch (piTableAction.type()) {
217 case ACTION:
218 return checkPiAction((PiAction) piTableAction, table);
219 default:
220 // FIXME: should we check? how?
221 return piTableAction;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900222
Yi Tseng82512da2017-08-16 19:46:36 -0700223 }
224 }
225
226 private static PiTableAction checkPiAction(PiAction piAction, PiTableModel table)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200227 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900228 // Table supports this action?
Carmelo Cascone87892e22017-11-13 16:01:29 -0800229 PiActionModel actionModel = table.action(piAction.id()).orElseThrow(
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200230 () -> new PiTranslationException(format("Not such action '%s' for table '%s'",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800231 piAction.id(), table.id())));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900232
233 // Is the number of runtime parameters correct?
234 if (actionModel.params().size() != piAction.parameters().size()) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200235 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900236 "Wrong number of runtime parameters for action '%s', expected %d but found %d",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800237 actionModel.id(), actionModel.params().size(), piAction.parameters().size()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900238 }
239
240 // Forge a new action instance with well-sized parameters.
241 // The same comment as in typeCheckFieldMatch() about duplicating field match instances applies here.
242 PiAction.Builder newActionBuilder = PiAction.builder().withId(piAction.id());
243 for (PiActionParam param : piAction.parameters()) {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800244 PiActionParamModel paramModel = actionModel.param(param.id())
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200245 .orElseThrow(() -> new PiTranslationException(format(
Carmelo Cascone87892e22017-11-13 16:01:29 -0800246 "Not such parameter '%s' for action '%s'", param.id(), actionModel)));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900247 try {
248 newActionBuilder.withParameter(new PiActionParam(param.id(),
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700249 param.value().fit(paramModel.bitWidth())));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900250 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200251 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900252 "Size mismatch for parameter '%s' of action '%s': %s",
253 param.id(), piAction.id(), e.getMessage()));
254 }
255 }
256
257 return newActionBuilder.build();
258 }
259
260 /**
261 * Builds a collection of PI field matches out of the given selector, optionally using the given interpreter. The
262 * field matches returned are guaranteed to be compatible for the given table model.
263 */
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200264 private static Collection<PiFieldMatch> translateFieldMatches(PiPipelineInterpreter interpreter,
265 TrafficSelector selector, PiTableModel tableModel)
266 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900267
Carmelo Cascone87892e22017-11-13 16:01:29 -0800268 Map<PiMatchFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900269
270 // 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 -0800271 Map<PiMatchFieldId, PiFieldMatch> piCriterionFields = selector.criteria().stream()
Carmelo Cascone00a59962017-06-16 17:51:49 +0900272 .filter(c -> c.type().equals(PROTOCOL_INDEPENDENT))
273 .map(c -> (PiCriterion) c)
274 .findFirst()
275 .map(PiCriterion::fieldMatches)
276 .map(c -> {
Carmelo Cascone87892e22017-11-13 16:01:29 -0800277 Map<PiMatchFieldId, PiFieldMatch> fieldMap = Maps.newHashMap();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900278 c.forEach(fieldMatch -> fieldMap.put(fieldMatch.fieldId(), fieldMatch));
279 return fieldMap;
280 })
281 .orElse(Maps.newHashMap());
282
283 Set<Criterion> translatedCriteria = Sets.newHashSet();
284 Set<Criterion> ignoredCriteria = Sets.newHashSet();
Carmelo Cascone87892e22017-11-13 16:01:29 -0800285 Set<PiMatchFieldId> usedPiCriterionFields = Sets.newHashSet();
286 Set<PiMatchFieldId> ignoredPiCriterionFields = Sets.newHashSet();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900287
Carmelo Cascone87892e22017-11-13 16:01:29 -0800288 for (PiMatchFieldModel fieldModel : tableModel.matchFields()) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900289
Carmelo Cascone87892e22017-11-13 16:01:29 -0800290 PiMatchFieldId fieldId = fieldModel.id();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900291
Carmelo Cascone87892e22017-11-13 16:01:29 -0800292 int bitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900293 int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);
294
295 Optional<Criterion.Type> criterionType =
296 interpreter == null
297 ? Optional.empty()
Carmelo Cascone87892e22017-11-13 16:01:29 -0800298 : interpreter.mapPiMatchFieldId(fieldId);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900299
300 Criterion criterion = criterionType.map(selector::getCriterion).orElse(null);
301
302 if (!piCriterionFields.containsKey(fieldId) && criterion == null) {
303 // Neither a field in PiCriterion is available nor a Criterion mapping is possible.
304 // Can ignore if the match is ternary or LPM.
305 switch (fieldModel.matchType()) {
306 case TERNARY:
307 // Wildcard the whole field.
308 fieldMatches.put(fieldId, new PiTernaryFieldMatch(
309 fieldId,
310 ImmutableByteSequence.ofZeros(fieldByteWidth),
311 ImmutableByteSequence.ofZeros(fieldByteWidth)));
312 break;
313 case LPM:
314 // LPM with prefix 0
315 fieldMatches.put(fieldId, new PiLpmFieldMatch(fieldId,
316 ImmutableByteSequence.ofZeros(fieldByteWidth),
317 0));
318 break;
319 // FIXME: Can we handle the case of RANGE or VALID match?
320 default:
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200321 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900322 "No value found for required match field '%s'", fieldId));
323 }
324 // Next field.
325 continue;
326 }
327
328 PiFieldMatch fieldMatch = null;
329
330 if (criterion != null) {
331 // Criterion mapping is possible for this field id.
332 try {
333 fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
334 translatedCriteria.add(criterion);
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200335 } catch (PiTranslationException ex) {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900336 // Ignore exception if the same field was found in PiCriterion.
337 if (piCriterionFields.containsKey(fieldId)) {
338 ignoredCriteria.add(criterion);
339 } else {
340 throw ex;
341 }
342 }
343 }
344
345 if (piCriterionFields.containsKey(fieldId)) {
346 // Field was found in PiCriterion.
347 if (fieldMatch != null) {
348 // Field was already translated from other criterion.
349 // Throw exception only if we are trying to match on different values of the same field...
350 if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200351 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900352 "Duplicate match field '%s': instance translated from criterion '%s' is different to " +
353 "what found in PiCriterion.", fieldId, criterion.type()));
354 }
355 ignoredPiCriterionFields.add(fieldId);
356 } else {
357 fieldMatch = piCriterionFields.get(fieldId);
358 fieldMatch = typeCheckFieldMatch(fieldMatch, fieldModel);
359 usedPiCriterionFields.add(fieldId);
360 }
361 }
362
363 fieldMatches.put(fieldId, fieldMatch);
364 }
365
366 // Check if all criteria have been translated.
367 StringJoiner skippedCriteriaJoiner = new StringJoiner(", ");
368 selector.criteria().stream()
369 .filter(c -> !c.type().equals(PROTOCOL_INDEPENDENT))
370 .filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c))
371 .forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
372 if (skippedCriteriaJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200373 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900374 "The following criteria cannot be translated for table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800375 tableModel.id(), skippedCriteriaJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900376 }
377
378 // Check if all fields found in PiCriterion have been used.
379 StringJoiner skippedPiFieldsJoiner = new StringJoiner(", ");
380 piCriterionFields.keySet().stream()
381 .filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k))
382 .forEach(k -> skippedPiFieldsJoiner.add(k.id()));
383 if (skippedPiFieldsJoiner.length() > 0) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200384 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900385 "The following PiCriterion field matches are not supported in table '%s': %s",
Carmelo Cascone87892e22017-11-13 16:01:29 -0800386 tableModel.id(), skippedPiFieldsJoiner.toString()));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900387 }
388
389 return fieldMatches.values();
390 }
391
Carmelo Cascone87892e22017-11-13 16:01:29 -0800392 private static PiFieldMatch typeCheckFieldMatch(PiFieldMatch fieldMatch, PiMatchFieldModel fieldModel)
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200393 throws PiTranslationException {
Carmelo Cascone00a59962017-06-16 17:51:49 +0900394
395 // Check parameter type and size
396 if (!fieldModel.matchType().equals(fieldMatch.type())) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200397 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900398 "Wrong match type for field '%s', expected %s, but found %s",
399 fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
400 }
401
Carmelo Cascone87892e22017-11-13 16:01:29 -0800402 int modelBitWidth = fieldModel.bitWidth();
Carmelo Cascone00a59962017-06-16 17:51:49 +0900403
404 /*
405 Here we try to be robust against wrong size fields with the goal of having PiCriterion independent of the
406 pipeline model. We duplicate the field match, fitting the byte sequences to the bit-width specified in the
407 model. This operation is expensive when performed for each field match of each flow rule, but should be
408 mitigated by the translation cache provided by PiFlowRuleTranslationServiceImpl.
409 */
410
411 try {
412 switch (fieldModel.matchType()) {
413 case EXACT:
414 return new PiExactFieldMatch(fieldMatch.fieldId(),
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700415 ((PiExactFieldMatch) fieldMatch).value().fit(modelBitWidth));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900416 case TERNARY:
417 return new PiTernaryFieldMatch(fieldMatch.fieldId(),
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700418 ((PiTernaryFieldMatch) fieldMatch).value().fit(modelBitWidth),
419 ((PiTernaryFieldMatch) fieldMatch).mask().fit(modelBitWidth));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900420 case LPM:
421 PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
422 if (lpmfield.prefixLength() > modelBitWidth) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200423 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900424 "Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
425 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
426 }
427 return new PiLpmFieldMatch(fieldMatch.fieldId(),
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700428 lpmfield.value().fit(modelBitWidth),
Carmelo Cascone00a59962017-06-16 17:51:49 +0900429 lpmfield.prefixLength());
430 case RANGE:
431 return new PiRangeFieldMatch(fieldMatch.fieldId(),
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700432 ((PiRangeFieldMatch) fieldMatch).lowValue().fit(modelBitWidth),
433 ((PiRangeFieldMatch) fieldMatch).highValue().fit(modelBitWidth));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900434 case VALID:
435 return fieldMatch;
436 default:
437 // Should never be here.
Ray Milkey986a47a2018-01-25 11:38:51 -0800438 throw new IllegalArgumentException(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900439 "Unrecognized match type " + fieldModel.matchType().name());
440 }
441 } catch (ByteSequenceTrimException e) {
Carmelo Cascone87b9b392017-10-02 18:33:20 +0200442 throw new PiTranslationException(format(
Carmelo Cascone00a59962017-06-16 17:51:49 +0900443 "Size mismatch for field %s: %s", fieldMatch.fieldId(), e.getMessage()));
444 }
445 }
446}