blob: 9b2b4a04f53ff87007c11d88d02a9067e7a1139b [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 org.apache.commons.lang3.tuple.Pair;
20import org.onlab.util.ImmutableByteSequence;
21import org.onosproject.net.pi.model.PiMatchType;
22
23import java.util.Optional;
24
25import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
Carmelo Cascone00a59962017-06-16 17:51:49 +090026
27/**
28 * Abstract implementation of a criterion translator that opportunistically tries to generate different types of match
29 * based on an initialization method. It throws an exception when a safe translation is not possible, e.g. when trying
30 * to translate a masked criterion to an exact match.
31 */
32abstract class AbstractCriterionTranslator implements CriterionTranslator {
33
34 private PiMatchType initType;
35 private int bitWidth;
36 private ImmutableByteSequence value;
37 private ImmutableByteSequence mask;
38 private Integer prefixLength;
39
40 /**
41 * Initializes the translator as an exact match.
42 *
43 * @param value exact match value
44 * @param bitWidth field bit-width
45 * @throws ByteSequenceTrimException if the match value cannot be trimmed to fit the field match bit-width
46 */
47 void initAsExactMatch(ImmutableByteSequence value, int bitWidth)
48 throws ByteSequenceTrimException {
Yi Tseng088f4ce2017-08-15 10:54:14 -070049 this.initType = PiMatchType.EXACT;
Carmelo Cascone8a571af2018-04-06 23:17:04 -070050 this.value = value.fit(bitWidth);
Carmelo Cascone00a59962017-06-16 17:51:49 +090051 this.bitWidth = bitWidth;
Carmelo Cascone00a59962017-06-16 17:51:49 +090052 }
53
54 /**
55 * Initializes the translator as a ternary match.
56 *
57 * @param value match value
58 * @param mask match mask
59 * @param bitWidth field bit-width
60 * @throws ByteSequenceTrimException if the match value or mask cannot be trimmed to fit the field match bit-width
61 */
62 void initAsTernaryMatch(ImmutableByteSequence value, ImmutableByteSequence mask, int bitWidth)
63 throws ByteSequenceTrimException {
Yi Tseng088f4ce2017-08-15 10:54:14 -070064 this.initType = PiMatchType.TERNARY;
Carmelo Cascone8a571af2018-04-06 23:17:04 -070065 this.value = value.fit(bitWidth);
66 this.mask = mask.fit(bitWidth);
Carmelo Cascone00a59962017-06-16 17:51:49 +090067 this.bitWidth = bitWidth;
Carmelo Cascone00a59962017-06-16 17:51:49 +090068 }
69
70 /**
71 * Initializes the translator as a longest-prefix match.
72 *
73 * @param value match value
74 * @param prefixLength prefix length
75 * @param bitWidth field bit-width
76 * @throws ByteSequenceTrimException if the match value cannot be trimmed to fit the field match bit-width
77 */
78 void initAsLpm(ImmutableByteSequence value, int prefixLength, int bitWidth)
79 throws ByteSequenceTrimException {
Yi Tseng088f4ce2017-08-15 10:54:14 -070080 this.initType = PiMatchType.LPM;
Carmelo Cascone8a571af2018-04-06 23:17:04 -070081 this.value = value.fit(bitWidth);
Carmelo Cascone00a59962017-06-16 17:51:49 +090082 this.prefixLength = prefixLength;
83 this.bitWidth = bitWidth;
Yi Tseng088f4ce2017-08-15 10:54:14 -070084 }
85
86 /**
87 * Computes the prefix padding size (in bits) based on value and bit width.
88 *
89 * @return prefix padding in bits
90 */
91 private int prefixPadding() {
92 return value.size() * Byte.SIZE - this.bitWidth;
Carmelo Cascone00a59962017-06-16 17:51:49 +090093 }
94
95 @Override
96 public ImmutableByteSequence exactMatch() throws CriterionTranslatorException {
97 switch (initType) {
98 case EXACT:
99 break;
100 case TERNARY:
101 ImmutableByteSequence exactMask = ImmutableByteSequence.ofOnes(value.size());
102 if (!mask.equals(exactMask)) {
103 throw new CriterionTranslator.CriterionTranslatorException(
104 "trying to use masked field as an exact match, but mask is not exact");
105 }
106 break;
107 case LPM:
108 if (prefixLength < bitWidth) {
109 throw new CriterionTranslator.CriterionTranslatorException(
110 "trying to use LPM field as an exact match, but prefix is not full");
111 }
112 break;
113 default:
Ray Milkey986a47a2018-01-25 11:38:51 -0800114 throw new IllegalArgumentException("Unrecognized init type " + initType.name());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900115 }
116 return value;
117 }
118
119 @Override
120 public Pair<ImmutableByteSequence, ImmutableByteSequence> ternaryMatch() {
121 switch (initType) {
122 case EXACT:
Yi Tseng088f4ce2017-08-15 10:54:14 -0700123 mask = ImmutableByteSequence.prefixZeros(value.size(), prefixPadding());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900124 break;
125 case TERNARY:
126 break;
127 case LPM:
128 mask = getMaskFromPrefixLength(prefixLength, value.size());
Yi Tsengdbe05602017-11-17 18:02:43 -0800129 break;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900130 default:
Ray Milkey986a47a2018-01-25 11:38:51 -0800131 throw new IllegalArgumentException("Unrecognized init type " + initType.name());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900132 }
133
134 return Pair.of(value, mask);
135 }
136
137 @Override
138 public Pair<ImmutableByteSequence, Integer> lpmMatch() throws CriterionTranslatorException {
139 switch (initType) {
140 case EXACT:
141 prefixLength = bitWidth;
142 break;
143 case TERNARY:
144 prefixLength = getPrefixLengthFromMask(mask).orElseThrow(
145 () -> new CriterionTranslatorException(
146 "trying to use masked field as a longest-prefix match, " +
147 "but mask is not equivalent to a prefix length"));
148 break;
149 case LPM:
150 break;
151 default:
Ray Milkey986a47a2018-01-25 11:38:51 -0800152 throw new IllegalArgumentException("Unrecognized init type " + initType.name());
Carmelo Cascone00a59962017-06-16 17:51:49 +0900153 }
154
155 return Pair.of(value, prefixLength);
156 }
157
158 /**
159 * Returns a bit mask equivalent to the given prefix length.
160 *
161 * @param prefixLength prefix length (in bits)
162 * @param maskSize mask size (in bytes)
163 * @return a byte sequence
164 */
165 private ImmutableByteSequence getMaskFromPrefixLength(int prefixLength, int maskSize) {
Yi Tsengdbe05602017-11-17 18:02:43 -0800166 return ImmutableByteSequence.prefixOnes(maskSize, prefixLength);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900167 }
168
169 /**
170 * Checks that the given mask is equivalent to a longest-prefix match and returns the prefix length. If not
171 * possible, the optional value will not be present.
172 *
173 * @param mask byte sequence
174 * @return optional prefix length
175 */
176 private Optional<Integer> getPrefixLengthFromMask(ImmutableByteSequence mask) {
Yi Tsengdbe05602017-11-17 18:02:43 -0800177 Integer prefixLength = 0;
178
179 byte[] byteArray = mask.asArray();
180 int byteArrayIndex = 0;
181 while (byteArrayIndex < byteArray.length && byteArray[byteArrayIndex] == (byte) 0xff) {
182 prefixLength += Byte.SIZE;
183 byteArrayIndex++;
184 }
185
186 byte byteVal = byteArray[byteArrayIndex];
187 while (byteVal != 0) {
188 if ((byteVal & Integer.MIN_VALUE) != Integer.MIN_VALUE) {
189 return Optional.empty();
190 }
191 prefixLength++;
192 byteVal <<= 1;
193 }
194
195 byteArrayIndex++;
196 while (byteArrayIndex < byteArray.length) {
197 if (byteArray[byteArrayIndex] != 0) {
198 return Optional.empty();
199 }
200 byteArrayIndex++;
201 }
202
203 return Optional.of(prefixLength);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900204 }
205}