| /* |
| * Copyright 2019-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.p4runtime.ctl.codec; |
| |
| import com.google.protobuf.ByteString; |
| import org.onlab.util.ImmutableByteSequence; |
| import org.onosproject.net.pi.model.PiMatchFieldId; |
| import org.onosproject.net.pi.model.PiPipeconf; |
| import org.onosproject.net.pi.runtime.PiExactFieldMatch; |
| import org.onosproject.net.pi.runtime.PiFieldMatch; |
| import org.onosproject.net.pi.runtime.PiLpmFieldMatch; |
| import org.onosproject.net.pi.runtime.PiOptionalFieldMatch; |
| import org.onosproject.net.pi.runtime.PiRangeFieldMatch; |
| import org.onosproject.net.pi.runtime.PiTernaryFieldMatch; |
| import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser; |
| import p4.config.v1.P4InfoOuterClass; |
| import p4.v1.P4RuntimeOuterClass; |
| |
| import static java.lang.String.format; |
| import static org.onlab.util.ImmutableByteSequence.copyAndFit; |
| import static org.onlab.util.ImmutableByteSequence.copyFrom; |
| import static org.onosproject.p4runtime.ctl.codec.Utils.assertPrefixLen; |
| import static org.onosproject.p4runtime.ctl.codec.Utils.assertSize; |
| import static org.onosproject.p4runtime.ctl.codec.Utils.sdnStringUnsupported; |
| |
| /** |
| * Codec for P4Runtime FieldMatch. Metadata is expected to be a Preamble for |
| * P4Info.Table. |
| */ |
| public final class FieldMatchCodec |
| extends AbstractCodec<PiFieldMatch, P4RuntimeOuterClass.FieldMatch, |
| P4InfoOuterClass.Preamble> { |
| |
| private static final String VALUE_OF_PREFIX = "value of "; |
| private static final String MASK_OF_PREFIX = "mask of "; |
| private static final String HIGH_RANGE_VALUE_OF_PREFIX = "high range value of "; |
| private static final String LOW_RANGE_VALUE_OF_PREFIX = "low range value of "; |
| |
| @Override |
| public P4RuntimeOuterClass.FieldMatch encode( |
| PiFieldMatch piFieldMatch, P4InfoOuterClass.Preamble tablePreamble, |
| PiPipeconf pipeconf, P4InfoBrowser browser) |
| throws CodecException, P4InfoBrowser.NotFoundException { |
| |
| P4RuntimeOuterClass.FieldMatch.Builder messageBuilder = P4RuntimeOuterClass |
| .FieldMatch.newBuilder(); |
| |
| // FIXME: check how field names for stacked headers are constructed in P4Runtime. |
| String fieldName = piFieldMatch.fieldId().id(); |
| P4InfoOuterClass.MatchField matchFieldInfo = browser.matchFields( |
| tablePreamble.getId()).getByName(fieldName); |
| String entityName = format("field match '%s' of table '%s'", |
| matchFieldInfo.getName(), tablePreamble.getName()); |
| int fieldId = matchFieldInfo.getId(); |
| int fieldBitwidth = matchFieldInfo.getBitwidth(); |
| boolean isSdnString = browser.isTypeString(matchFieldInfo.getTypeName()); |
| |
| messageBuilder.setFieldId(fieldId); |
| |
| switch (piFieldMatch.type()) { |
| case EXACT: |
| PiExactFieldMatch fieldMatch = (PiExactFieldMatch) piFieldMatch; |
| ByteString exactValue = ByteString.copyFrom(fieldMatch.value().asReadOnlyBuffer()); |
| if (!isSdnString) { |
| assertSize(VALUE_OF_PREFIX + entityName, exactValue, fieldBitwidth); |
| } |
| return messageBuilder.setExact( |
| P4RuntimeOuterClass.FieldMatch.Exact |
| .newBuilder() |
| .setValue(exactValue) |
| .build()) |
| .build(); |
| case TERNARY: |
| PiTernaryFieldMatch ternaryMatch = (PiTernaryFieldMatch) piFieldMatch; |
| ByteString ternaryValue = ByteString.copyFrom(ternaryMatch.value().asReadOnlyBuffer()); |
| ByteString ternaryMask = ByteString.copyFrom(ternaryMatch.mask().asReadOnlyBuffer()); |
| if (isSdnString) { |
| sdnStringUnsupported(entityName, piFieldMatch.type()); |
| } |
| assertSize(VALUE_OF_PREFIX + entityName, ternaryValue, fieldBitwidth); |
| assertSize(MASK_OF_PREFIX + entityName, ternaryMask, fieldBitwidth); |
| return messageBuilder.setTernary( |
| P4RuntimeOuterClass.FieldMatch.Ternary |
| .newBuilder() |
| .setValue(ternaryValue) |
| .setMask(ternaryMask) |
| .build()) |
| .build(); |
| case LPM: |
| PiLpmFieldMatch lpmMatch = (PiLpmFieldMatch) piFieldMatch; |
| ByteString lpmValue = ByteString.copyFrom(lpmMatch.value().asReadOnlyBuffer()); |
| int lpmPrefixLen = lpmMatch.prefixLength(); |
| if (isSdnString) { |
| sdnStringUnsupported(entityName, piFieldMatch.type()); |
| } |
| assertSize(VALUE_OF_PREFIX + entityName, lpmValue, fieldBitwidth); |
| assertPrefixLen(entityName, lpmPrefixLen, fieldBitwidth); |
| return messageBuilder.setLpm( |
| P4RuntimeOuterClass.FieldMatch.LPM.newBuilder() |
| .setValue(lpmValue) |
| .setPrefixLen(lpmPrefixLen) |
| .build()) |
| .build(); |
| case RANGE: |
| PiRangeFieldMatch rangeMatch = (PiRangeFieldMatch) piFieldMatch; |
| ByteString rangeHighValue = ByteString.copyFrom(rangeMatch.highValue().asReadOnlyBuffer()); |
| ByteString rangeLowValue = ByteString.copyFrom(rangeMatch.lowValue().asReadOnlyBuffer()); |
| if (isSdnString) { |
| sdnStringUnsupported(entityName, piFieldMatch.type()); |
| } |
| assertSize(HIGH_RANGE_VALUE_OF_PREFIX + entityName, rangeHighValue, fieldBitwidth); |
| assertSize(LOW_RANGE_VALUE_OF_PREFIX + entityName, rangeLowValue, fieldBitwidth); |
| return messageBuilder.setRange( |
| P4RuntimeOuterClass.FieldMatch.Range.newBuilder() |
| .setHigh(rangeHighValue) |
| .setLow(rangeLowValue) |
| .build()) |
| .build(); |
| case OPTIONAL: |
| PiOptionalFieldMatch optionalMatch = (PiOptionalFieldMatch) piFieldMatch; |
| ByteString optionalValue = ByteString.copyFrom(optionalMatch.value().asReadOnlyBuffer()); |
| if (!isSdnString) { |
| assertSize(VALUE_OF_PREFIX + entityName, optionalValue, fieldBitwidth); |
| } |
| return messageBuilder.setOptional( |
| P4RuntimeOuterClass.FieldMatch.Optional.newBuilder() |
| .setValue(optionalValue) |
| .build()) |
| .build(); |
| default: |
| throw new CodecException(format( |
| "Building of match type %s not implemented", piFieldMatch.type())); |
| } |
| } |
| |
| @Override |
| public PiFieldMatch decode( |
| P4RuntimeOuterClass.FieldMatch message, P4InfoOuterClass.Preamble tablePreamble, |
| PiPipeconf pipeconf, P4InfoBrowser browser) |
| throws CodecException, P4InfoBrowser.NotFoundException { |
| |
| final P4InfoOuterClass.MatchField matchField = |
| browser.matchFields(tablePreamble.getId()) |
| .getById(message.getFieldId()); |
| final int fieldBitwidth = matchField.getBitwidth(); |
| final PiMatchFieldId headerFieldId = PiMatchFieldId.of(matchField.getName()); |
| final boolean isSdnString = browser.isTypeString(matchField.getTypeName()); |
| |
| final P4RuntimeOuterClass.FieldMatch.FieldMatchTypeCase typeCase = message.getFieldMatchTypeCase(); |
| try { |
| switch (typeCase) { |
| case EXACT: |
| P4RuntimeOuterClass.FieldMatch.Exact exactFieldMatch = message.getExact(); |
| final ImmutableByteSequence exactValue; |
| if (isSdnString) { |
| exactValue = copyFrom(new String(exactFieldMatch.getValue().toByteArray())); |
| } else { |
| exactValue = copyAndFit( |
| exactFieldMatch.getValue().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| } |
| return new PiExactFieldMatch(headerFieldId, exactValue); |
| case TERNARY: |
| P4RuntimeOuterClass.FieldMatch.Ternary ternaryFieldMatch = message.getTernary(); |
| ImmutableByteSequence ternaryValue = copyAndFit( |
| ternaryFieldMatch.getValue().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| ImmutableByteSequence ternaryMask = copyAndFit( |
| ternaryFieldMatch.getMask().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| return new PiTernaryFieldMatch(headerFieldId, ternaryValue, ternaryMask); |
| case LPM: |
| P4RuntimeOuterClass.FieldMatch.LPM lpmFieldMatch = message.getLpm(); |
| ImmutableByteSequence lpmValue = copyAndFit( |
| lpmFieldMatch.getValue().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| int lpmPrefixLen = lpmFieldMatch.getPrefixLen(); |
| return new PiLpmFieldMatch(headerFieldId, lpmValue, lpmPrefixLen); |
| case RANGE: |
| P4RuntimeOuterClass.FieldMatch.Range rangeFieldMatch = message.getRange(); |
| ImmutableByteSequence rangeHighValue = copyAndFit( |
| rangeFieldMatch.getHigh().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| ImmutableByteSequence rangeLowValue = copyAndFit( |
| rangeFieldMatch.getLow().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| return new PiRangeFieldMatch(headerFieldId, rangeLowValue, rangeHighValue); |
| case OPTIONAL: |
| P4RuntimeOuterClass.FieldMatch.Optional optionalFieldMatch = message.getOptional(); |
| final ImmutableByteSequence optionalValue; |
| if (isSdnString) { |
| optionalValue = copyFrom(new String(optionalFieldMatch.getValue().toByteArray())); |
| } else { |
| optionalValue = copyAndFit( |
| optionalFieldMatch.getValue().asReadOnlyByteBuffer(), |
| fieldBitwidth); |
| } |
| return new PiOptionalFieldMatch(headerFieldId, optionalValue); |
| default: |
| throw new CodecException(format( |
| "Decoding of field match type '%s' not implemented", typeCase.name())); |
| } |
| } catch (ImmutableByteSequence.ByteSequenceTrimException e) { |
| throw new CodecException(e.getMessage()); |
| } |
| } |
| } |