blob: 1e357c4e1db654b55463a7646aca6c97be9c84e1 [file] [log] [blame]
Carmelo Cascone17fc9e42016-05-31 11:29:21 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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.bmv2.api.runtime;
18
Carmelo Cascone9e39e312016-06-16 14:47:09 -070019import com.google.common.annotations.Beta;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070020import com.google.common.base.MoreObjects;
21import com.google.common.base.Objects;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070022import com.google.common.collect.Maps;
23import org.apache.commons.lang3.tuple.Pair;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070024import org.onlab.util.KryoNamespace;
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070025import org.onosproject.bmv2.api.context.Bmv2Configuration;
26import org.onosproject.bmv2.api.context.Bmv2FieldTypeModel;
27import org.onosproject.bmv2.api.context.Bmv2HeaderModel;
28import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070029import org.onosproject.net.flow.AbstractExtension;
30import org.onosproject.net.flow.criteria.ExtensionSelector;
31import org.onosproject.net.flow.criteria.ExtensionSelectorType;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070032import org.onosproject.store.serializers.KryoNamespaces;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070033
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070034import java.nio.ByteBuffer;
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070035import java.util.Collections;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070036import java.util.Map;
37
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070038import static com.google.common.base.Preconditions.*;
39import static org.onlab.util.ImmutableByteSequence.copyFrom;
40import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070041
42/**
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070043 * Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters. Match parameters are
44 * encoded using a map where the keys are expected to be field names formatted as {@code headerName.fieldName}
45 * (e.g. {@code ethernet.dstAddr}).
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070046 */
Carmelo Cascone9e39e312016-06-16 14:47:09 -070047@Beta
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070048public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
49
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070050 private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
51 .register(KryoNamespaces.API)
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070052 .register(Bmv2ExactMatchParam.class)
53 .register(Bmv2TernaryMatchParam.class)
54 .register(Bmv2LpmMatchParam.class)
55 .register(Bmv2ValidMatchParam.class)
56 .build();
57
58 private Map<String, Bmv2MatchParam> parameterMap;
59
60 /**
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070061 * Creates a new BMv2 extension selector for the given match parameters map.
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070062 *
63 * @param paramMap a map
64 */
Carmelo Cascone0ec92f12016-06-17 14:41:40 -070065 private Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
66 this.parameterMap = paramMap;
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070067 }
68
69 /**
70 * Returns the match parameters map of this selector.
71 *
72 * @return a match parameter map
73 */
74 public Map<String, Bmv2MatchParam> parameterMap() {
75 return parameterMap;
76 }
77
78
79 @Override
80 public ExtensionSelectorType type() {
81 return ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS.type();
82 }
83
84 @Override
85 public byte[] serialize() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070086 return APP_KRYO.serialize(parameterMap);
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070087 }
88
89 @Override
90 public void deserialize(byte[] data) {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -070091 this.parameterMap = APP_KRYO.deserialize(data);
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070092 }
93
94 @Override
95 public int hashCode() {
96 return Objects.hashCode(parameterMap);
97 }
98
99 @Override
100 public boolean equals(Object obj) {
101 if (this == obj) {
102 return true;
103 }
104 if (obj == null || getClass() != obj.getClass()) {
105 return false;
106 }
107 final Bmv2ExtensionSelector other = (Bmv2ExtensionSelector) obj;
108 return Objects.equal(this.parameterMap, other.parameterMap);
109 }
110
111 @Override
112 public String toString() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700113 MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this);
114 parameterMap.forEach((name, param) -> {
115 switch (param.type()) {
116 case EXACT:
117 Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) param;
118 helper.add(name, e.value());
119 break;
120 case TERNARY:
121 Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) param;
122 helper.add(name, t.value() + "&&&" + t.mask());
123 break;
124 case LPM:
125 Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) param;
126 helper.add(name, l.value() + "/" + String.valueOf(l.prefixLength()));
127 break;
128 case VALID:
129 Bmv2ValidMatchParam v = (Bmv2ValidMatchParam) param;
130 helper.add(name, v.flag() ? "VALID" : "NOT_VALID");
131 break;
132 default:
133 helper.add(name, param);
134 break;
135 }
136 });
137 return helper.toString();
Carmelo Cascone17fc9e42016-05-31 11:29:21 -0700138 }
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700139
140 /**
141 * Returns a new, empty BMv2 extension selector.
142 *
143 * @return a BMv2 extension treatment
144 */
145 public static Bmv2ExtensionSelector empty() {
Carmelo Casconeeeeabec2016-06-23 13:11:53 -0700146 return new Bmv2ExtensionSelector(Collections.emptyMap());
Carmelo Cascone0ec92f12016-06-17 14:41:40 -0700147 }
148
149 /**
150 * Returns a new builder of BMv2 extension selectors.
151 *
152 * @return a builder
153 */
154 public static Builder builder() {
155 return new Builder();
156 }
157
158 /**
159 * Builder of BMv2 extension selectors.
160 * <p>
161 * Match parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
162 * {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
163 * configuration.
164 */
165 public static final class Builder {
166
167 private final Map<Pair<String, String>, Bmv2MatchParam> parameterMap = Maps.newHashMap();
168 private Bmv2Configuration configuration;
169
170 private Builder() {
171 // ban constructor.
172 }
173
174 /**
175 * Sets the BMv2 configuration to format the match parameters of the selector.
176 *
177 * @param config a BMv2 configuration
178 * @return this
179 */
180 public Builder forConfiguration(Bmv2Configuration config) {
181 this.configuration = config;
182 return this;
183 }
184
185 /**
186 * Adds an exact match parameter for the given header field and value.
187 *
188 * @param headerName a string value
189 * @param fieldName a string value
190 * @param value a short value
191 * @return this
192 */
193 public Builder matchExact(String headerName, String fieldName, short value) {
194 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
195 checkNotNull(fieldName, "field name cannot be null")),
196 exact(value));
197 return this;
198 }
199
200 /**
201 * Adds an exact match parameter for the given header field and value.
202 *
203 * @param headerName a string value
204 * @param fieldName a string value
205 * @param value an integer value
206 * @return this
207 */
208 public Builder matchExact(String headerName, String fieldName, int value) {
209 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
210 checkNotNull(fieldName, "field name cannot be null")),
211 exact(value));
212 return this;
213 }
214
215 /**
216 * Adds an exact match parameter for the given header field and value.
217 *
218 * @param headerName a string value
219 * @param fieldName a string value
220 * @param value a long value
221 * @return this
222 */
223 public Builder matchExact(String headerName, String fieldName, long value) {
224 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
225 checkNotNull(fieldName, "field name cannot be null")),
226 exact(value));
227 return this;
228 }
229
230 /**
231 * Adds an exact match parameter for the given header field and value.
232 *
233 * @param headerName a string value
234 * @param fieldName a string value
235 * @param value a byte array
236 * @return this
237 */
238 public Builder matchExact(String headerName, String fieldName, byte[] value) {
239 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
240 checkNotNull(fieldName, "field name cannot be null")),
241 exact(value));
242 return this;
243 }
244
245 /**
246 * Adds a ternary match parameter for the given header field, value and mask.
247 *
248 * @param headerName a string value
249 * @param fieldName a string value
250 * @param value a short value
251 * @param mask a short value
252 * @return this
253 */
254 public Builder matchTernary(String headerName, String fieldName, short value, short mask) {
255 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
256 checkNotNull(fieldName, "field name cannot be null")),
257 ternary(value, mask));
258 return this;
259 }
260 /**
261 * Adds a ternary match parameter for the given header field, value and mask.
262 *
263 * @param headerName a string value
264 * @param fieldName a string value
265 * @param value an integer value
266 * @param mask an integer value
267 * @return this
268 */
269 public Builder matchTernary(String headerName, String fieldName, int value, int mask) {
270 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
271 checkNotNull(fieldName, "field name cannot be null")),
272 ternary(value, mask));
273 return this;
274 }
275 /**
276 * Adds a ternary match parameter for the given header field, value and mask.
277 *
278 * @param headerName a string value
279 * @param fieldName a string value
280 * @param value a long value
281 * @param mask a long value
282 * @return this
283 */
284 public Builder matchTernary(String headerName, String fieldName, long value, long mask) {
285 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
286 checkNotNull(fieldName, "field name cannot be null")),
287 ternary(value, mask));
288 return this;
289 }
290 /**
291 * Adds a ternary match parameter for the given header field, value and mask.
292 *
293 * @param headerName a string value
294 * @param fieldName a string value
295 * @param value a byte array
296 * @param mask a byte array
297 * @return this
298 */
299 public Builder matchTernary(String headerName, String fieldName, byte[] value, byte[] mask) {
300 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
301 checkNotNull(fieldName, "field name cannot be null")),
302 ternary(value, mask));
303 return this;
304 }
305
306 /**
307 * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
308 *
309 * @param headerName a string value
310 * @param fieldName a string value
311 * @param value a short value
312 * @param prefixLength an integer value
313 * @return this
314 */
315 public Builder matchLpm(String headerName, String fieldName, short value, int prefixLength) {
316 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
317 checkNotNull(fieldName, "field name cannot be null")),
318 lpm(value, prefixLength));
319 return this;
320 }
321 /**
322 * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
323 *
324 * @param headerName a string value
325 * @param fieldName a string value
326 * @param value an integer value
327 * @param prefixLength an integer value
328 * @return this
329 */
330 public Builder matchLpm(String headerName, String fieldName, int value, int prefixLength) {
331 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
332 checkNotNull(fieldName, "field name cannot be null")),
333 lpm(value, prefixLength));
334 return this;
335 }
336 /**
337 * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
338 *
339 * @param headerName a string value
340 * @param fieldName a string value
341 * @param value a long value
342 * @param prefixLength an integer value
343 * @return this
344 */
345 public Builder matchLpm(String headerName, String fieldName, long value, int prefixLength) {
346 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
347 checkNotNull(fieldName, "field name cannot be null")),
348 lpm(value, prefixLength));
349 return this;
350 }
351 /**
352 * Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
353 *
354 * @param headerName a string value
355 * @param fieldName a string value
356 * @param value a byte array
357 * @param prefixLength an integer value
358 * @return this
359 */
360 public Builder matchLpm(String headerName, String fieldName, byte[] value, int prefixLength) {
361 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
362 checkNotNull(fieldName, "field name cannot be null")),
363 lpm(value, prefixLength));
364 return this;
365 }
366
367 /**
368 * Adds a valid match parameter for the given header field.
369 *
370 * @param headerName a string value
371 * @param fieldName a string value
372 * @param flag a boolean value
373 * @return this
374 */
375 public Builder matchValid(String headerName, String fieldName, boolean flag) {
376 parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
377 checkNotNull(fieldName, "field name cannot be null")),
378 new Bmv2ValidMatchParam(flag));
379 return this;
380 }
381
382 /**
383 * Returns a new BMv2 extension selector.
384 *
385 * @return a BMv2 extension selector
386 * @throws NullPointerException if a given header or field name is not defined in the given configuration
387 * @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
388 * when trying to fit an integer value into a smaller, fixed-length parameter
389 * produces overflow.
390 */
391 public Bmv2ExtensionSelector build() {
392 checkNotNull(configuration, "configuration cannot be null");
393 checkState(parameterMap.size() > 0, "parameter map cannot be empty");
394
395 final Map<String, Bmv2MatchParam> newParameterMap = Maps.newHashMap();
396
397 for (Pair<String, String> key : parameterMap.keySet()) {
398
399 String headerName = key.getLeft();
400 String fieldName = key.getRight();
401
402 Bmv2HeaderModel headerModel = configuration.header(headerName);
403 checkNotNull(headerModel, "no such a header in configuration", headerName);
404
405 Bmv2FieldTypeModel fieldModel = headerModel.type().field(fieldName);
406 checkNotNull(fieldModel, "no such a field in configuration", key);
407
408 int bitWidth = fieldModel.bitWidth();
409
410 Bmv2MatchParam oldParam = parameterMap.get(key);
411 Bmv2MatchParam newParam = null;
412
413 try {
414 switch (oldParam.type()) {
415 case EXACT:
416 Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) oldParam;
417 newParam = new Bmv2ExactMatchParam(fitByteSequence(e.value(), bitWidth));
418 break;
419 case TERNARY:
420 Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) oldParam;
421 newParam = new Bmv2TernaryMatchParam(fitByteSequence(t.value(), bitWidth),
422 fitByteSequence(t.mask(), bitWidth));
423 break;
424 case LPM:
425 Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) oldParam;
426 checkArgument(l.prefixLength() <= bitWidth, "LPM parameter has prefix length too long",
427 key);
428 newParam = new Bmv2LpmMatchParam(fitByteSequence(l.value(), bitWidth),
429 l.prefixLength());
430 break;
431 case VALID:
432 newParam = oldParam;
433 break;
434 default:
435 throw new RuntimeException("Match parameter type not supported: " + oldParam.type());
436 }
437 } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
438 throw new IllegalArgumentException(e.getMessage() + " [" + key + "]");
439 }
440 // FIXME: should put the pair object instead of building a new string for the key.
441 newParameterMap.put(headerName + "." + fieldName, newParam);
442 }
443
444 return new Bmv2ExtensionSelector(newParameterMap);
445 }
446
447 private static Bmv2MatchParam exact(Object value) {
448 return new Bmv2ExactMatchParam(copyFrom(bb(value)));
449 }
450
451 private static Bmv2MatchParam ternary(Object value, Object mask) {
452 return new Bmv2TernaryMatchParam(copyFrom(bb(value)), copyFrom(bb(mask)));
453 }
454
455 private static Bmv2MatchParam lpm(Object value, int prefixLength) {
456 return new Bmv2LpmMatchParam(copyFrom(bb(value)), prefixLength);
457 }
458
459 private static ByteBuffer bb(Object value) {
460 if (value instanceof Short) {
461 return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
462 } else if (value instanceof Integer) {
463 return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
464 } else if (value instanceof Long) {
465 return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
466 } else if (value instanceof byte[]) {
467 byte[] bytes = (byte[]) value;
468 return ByteBuffer.allocate(bytes.length).put(bytes);
469 } else {
470 // Never here.
471 return null;
472 }
473 }
474 }
Carmelo Cascone17fc9e42016-05-31 11:29:21 -0700475}