2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.match;
11 import java.io.Serializable;
12 import java.net.Inet4Address;
13 import java.net.Inet6Address;
14 import java.net.InetAddress;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.Map.Entry;
23 import java.util.TreeMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ConcurrentHashMap;
27 import javax.xml.bind.annotation.XmlAccessType;
28 import javax.xml.bind.annotation.XmlAccessorType;
29 import javax.xml.bind.annotation.XmlElement;
30 import javax.xml.bind.annotation.XmlRootElement;
32 import org.opendaylight.controller.sal.core.Property;
33 import org.opendaylight.controller.sal.utils.EtherTypes;
34 import org.opendaylight.controller.sal.utils.IPProtocols;
35 import org.opendaylight.controller.sal.utils.NetUtils;
38 * Represents the generic match criteria for a network frame/packet/message
39 * It contains a collection of individual field match
43 @XmlAccessorType(XmlAccessType.NONE)
45 public class Match implements Cloneable, Serializable {
46 private static final long serialVersionUID = 1L;
47 private static final Map<MatchType, MatchType> reversableMatches;
49 Map<MatchType, MatchType> map = new HashMap<MatchType, MatchType>();
50 map.put(MatchType.DL_SRC, MatchType.DL_DST);
51 map.put(MatchType.DL_DST, MatchType.DL_SRC);
52 map.put(MatchType.NW_SRC, MatchType.NW_DST);
53 map.put(MatchType.NW_DST, MatchType.NW_SRC);
54 map.put(MatchType.TP_SRC, MatchType.TP_DST);
55 map.put(MatchType.TP_DST, MatchType.TP_SRC);
56 reversableMatches = Collections.unmodifiableMap(map);
58 private Map<MatchType, MatchField> fields;
59 private int matches; // concise way to tell which fields the match
60 // is set for (may remove if not needed)
61 private ConcurrentMap<String, Property> props;
64 fields = new HashMap<MatchType, MatchField>();
68 public Match(Match match) {
69 fields = new HashMap<MatchType, MatchField>(match.fields);
70 matches = match.matches;
74 * Gets the list of metadata currently registered with this match
76 * @return List of metadata currently registered
78 public List <Property> getMetadatas() {
79 if (this.props != null) {
80 // Return all the values in the map
81 Collection res = this.props.values();
83 return Collections.emptyList();
85 return new ArrayList<Property>(res);
87 return Collections.emptyList();
91 * Gets the metadata registered with a name if present
93 * @param name the name of the property to be extracted
95 * @return List of metadata currently registered
97 public Property getMetadata(String name) {
101 if (this.props != null) {
102 // Return the Property associated to the name
103 return this.props.get(name);
109 * Sets the metadata associated to a name. If the name or prop is NULL,
110 * an exception NullPointerException will be raised.
112 * @param name the name of the property to be set
113 * @param prop, property to be set
115 public void setMetadata(String name, Property prop) {
116 if (this.props == null) {
117 props = new ConcurrentHashMap<String, Property>();
120 if (this.props != null) {
121 this.props.put(name, prop);
126 * Remove the metadata associated to a name. If the name is NULL,
127 * nothing will be removed.
129 * @param name the name of the property to be set
130 * @param prop, property to be set
132 * @return List of metadata currently registered
134 public void removeMetadata(String name) {
135 if (this.props == null) {
139 if (this.props != null) {
140 this.props.remove(name);
142 // It's intentional to keep the this.props still allocated
143 // till the parent data structure will be alive, so to avoid
144 // unnecessary allocation/deallocation, even if it's holding
149 * Generic setter for frame/packet/message's header fields against which to match
150 * Note: For MAC addresses, please pass the cloned value to this function
152 * @param type packet's header field type
153 * @param value field's value to assign to the match
154 * @param mask field's bitmask to apply to the match (has to be of the same class type of value)
156 public void setField(MatchType type, Object value, Object mask) {
157 MatchField field = new MatchField(type, value, mask);
158 if (field.isValid()) {
159 fields.put(type, field);
160 matches |= type.getIndex();
165 * Generic setter for frame/packet/message's header fields against which to match
166 * Note: For MAC addresses, please pass the cloned value to this function
168 * @param type packet's header field type
169 * @param value field's value to assign to the match
171 public void setField(MatchType type, Object value) {
172 MatchField field = new MatchField(type, value);
173 if (field.isValid()) {
174 fields.put(type, field);
175 matches |= type.getIndex();
180 * Generic setter for frame/packet/message's header field against which to match
182 * @param field the fields parameters as MAtchField object
184 public void setField(MatchField field) {
185 if (field.isValid()) {
186 fields.put(field.getType(), field);
187 matches |= field.getType().getIndex();
192 * Generic method to clear a field from the match
194 public void clearField(MatchType type) {
196 matches &= ~type.getIndex();
200 * Generic getter for fields against which the match is programmed
202 * @param type frame/packet/message's header field type
205 public MatchField getField(MatchType type) {
206 return fields.get(type);
210 * Returns the fields the match is set for in a bitmask fashion
211 * Each bit represents a field the match is configured for
213 * @return the 32 bit long mask (Refer to {@code}org.opendaylight.controller.sal.match.MatchElement)
215 public int getMatches() {
220 * Returns the list of MatchType fields the match is set for
222 * @return List of individual MatchType fields.
224 public List<MatchType> getMatchesList() {
225 return new ArrayList<MatchType>(fields.keySet());
229 * Returns the list of MatchFields the match is set for
231 * @return List of individual MatchField values.
233 @XmlElement(name="matchField")
234 public List<MatchField> getMatchFields() {
235 return new ArrayList<MatchField>(fields.values());
239 * Returns whether this match is for an IPv6 flow
241 public boolean isIPv6() {
242 return (isPresent(MatchType.DL_TYPE)
243 && ((Short) getField(MatchType.DL_TYPE).getValue())
244 .equals(EtherTypes.IPv6.shortValue())
245 || isPresent(MatchType.NW_PROTO)
246 && ((Byte) getField(MatchType.NW_PROTO).getValue())
247 .equals(IPProtocols.IPV6ICMP.byteValue())
248 || isPresent(MatchType.NW_SRC)
249 && getField(MatchType.NW_SRC).getValue() instanceof Inet6Address || isPresent(MatchType.NW_DST)
250 && getField(MatchType.NW_DST).getValue() instanceof Inet6Address);
254 * Returns whether this match is for an IPv4 flow
256 public boolean isIPv4() {
261 * Returns whether for the specified field type the match is to be considered "any"
262 * Equivalent to say this match does not care about the value of the specified field
267 public boolean isAny(MatchType type) {
268 //return ((fields.get(type) == null) || (fields.get(type).getBitMask() == 0L));
269 return !fields.containsKey(type);
273 * Returns whether a match for the specified field type is configured
278 public boolean isPresent(MatchType type) {
279 return (fields.get(type) != null);
283 public Match clone() {
286 cloned = (Match) super.clone();
287 cloned.matches = matches;
288 cloned.fields = new HashMap<MatchType, MatchField>();
289 for (Entry<MatchType, MatchField> entry : this.fields.entrySet()) {
290 cloned.fields.put(entry.getKey(), entry.getValue().clone());
292 } catch (CloneNotSupportedException e) {
293 throw new RuntimeException(e);
299 * Returns a reversed version of this match
300 * For example, in the reversed version the network source and destination
301 * addresses will be exchanged. Non symmetric match field will not be
302 * copied over into the reversed match version, like input port.
306 public Match reverse() {
307 // Copy over all fields
308 Match reverse = this.clone();
310 // Flip symmetric fields
311 for (Map.Entry<MatchType, MatchType> entry : Match.reversableMatches.entrySet()) {
312 MatchType from = entry.getKey();
313 MatchType to = entry.getValue();
314 if (this.isPresent(from)) {
315 reverse.setField(to, this.getField(from).getValue(), this.getField(from).getMask());
316 if (!this.isPresent(to)) {
317 reverse.clearField(from);
322 // Reset asymmetric fields
323 reverse.clearField(MatchType.IN_PORT);
329 * Check whether the current match conflicts with the passed filter match
330 * This match conflicts with the filter if for at least a MatchType defined
331 * in the filter match, the respective MatchFields differ or are not
334 * In other words the function returns true if the set of packets described
335 * by one match and the set of packets described by the other match are
336 * disjoint. Equivalently, if the intersection of the two sets of packets
337 * described by the two matches is an empty.
339 * For example, Let's suppose the filter has the following MatchFields:
341 * NW_DST = 172.20.30.110/24
343 * while this match has the following MatchFields:
345 * NW_DST = 172.20.30.45/24
348 * Then the function would return false as the two Match are not
351 * Note: the mask value is taken into account only for MatchType.NW_SRC and
355 * the Match describing the filter
356 * @return true if the set of packets described by one match and the set of
357 * packets described by the other match are disjoint, false
360 public boolean conflictWithFilter(Match filter) {
361 return !this.intersetcs(filter);
365 * Merge the current Match fields with the fields of the filter Match. A
366 * check is first run to see if this Match is compatible with the filter
367 * Match. If it is not, the merge is not attempted.
369 * The result is the match object representing the intersection of the set
370 * of packets described by this match with the set of packets described by
371 * the filter match. If the intersection of the two sets is empty, the
372 * return match will be null.
375 * the match with which attempting the merge
376 * @return a new Match object describing the set of packets represented by
377 * the intersection of this and the filter matches. null if the
378 * intersection is empty.
380 public Match mergeWithFilter(Match filter) {
381 return this.getIntersection(filter);
385 * Return the match representing the intersection of the set of packets
386 * described by this match with the set of packets described by the other
387 * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
388 * m.getIntersection(o) == o where u is an empty match (universal set, all
389 * packets) and o is the null match (empty set).
392 * the match with which computing the intersection
393 * @return a new Match object representing the intersection of the set of
394 * packets described by this match with the set of packets described
395 * by the other match. null when the intersection is the empty set.
397 public Match getIntersection(Match other) {
398 // If no intersection, return the empty set
399 if (!this.intersetcs(other)) {
402 // Check if any of the two is the universal match
403 if (this.getMatches() == 0) {
404 return other.clone();
406 if (other.getMatches() == 0) {
409 // Derive the intersection
410 Match intersection = new Match();
411 for (MatchType type : MatchType.values()) {
412 if (this.isAny(type) && other.isAny(type)) {
415 if (this.isAny(type)) {
416 intersection.setField(other.getField(type).clone());
418 } else if (other.isAny(type)) {
419 intersection.setField(this.getField(type).clone());
422 // Either they are equal or it is about IP address
424 // When it is about IP address, take the wider prefix address
428 MatchField thisField = this.getField(type);
429 MatchField otherField = other.getField(type);
430 InetAddress thisAddress = (InetAddress) thisField.getValue();
431 InetAddress otherAddress = (InetAddress) otherField.getValue();
432 InetAddress thisMask = (InetAddress) thisField.getMask();
433 InetAddress otherMask = (InetAddress) otherField.getMask();
435 int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
436 .getSubnetMaskLength(thisMask);
437 int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
438 .getSubnetMaskLength(otherMask);
439 if (thisMaskLen < otherMaskLen) {
440 intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen),
443 intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen),
448 // this and other match field are equal for this type, pick this
450 intersection.setField(this.getField(type).clone());
457 * Checks whether the intersection of the set of packets described by this
458 * match with the set of packets described by the other match is non empty
460 * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
462 * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
464 * then their respective matching packets set intersection is non empty:
465 * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
468 * the other match with which testing the intersection
469 * @return true if the intersection of the respective matching packets sets
472 public boolean intersetcs(Match other) {
473 // No intersection with the empty set
477 // Always intersection with the universal set
478 if (this.getMatches() == 0 || other.getMatches() == 0) {
481 // Iterate through the MatchType defined in the filter
482 for (MatchType type : MatchType.values()) {
483 if (this.isAny(type) || other.isAny(type)) {
487 MatchField thisField = this.getField(type);
488 MatchField otherField = other.getField(type);
493 if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
499 InetAddress thisAddress = (InetAddress) thisField.getValue();
500 InetAddress otherAddress = (InetAddress) otherField.getValue();
502 if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
503 || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
506 InetAddress thisMask = (InetAddress) thisField.getMask();
507 InetAddress otherMask = (InetAddress) otherField.getMask();
508 if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
509 && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
514 if (!thisField.getValue().equals(otherField.getValue())) {
523 public int hashCode() {
524 final int prime = 31;
526 if (this.fields == null) {
527 result = prime * result;
529 // use a tree map as the order of hashMap is not guaranteed.
530 // 2 Match objects with fields in different order are still equal.
531 // Hence the hashCode should be the same too.
532 TreeMap<MatchType, MatchField> tm = new TreeMap<MatchType, MatchField>(this.fields);
533 for (MatchType field : tm.keySet()) {
534 MatchField f = tm.get(field);
535 int fieldHashCode = (field==null ? 0 : field.calculateConsistentHashCode()) ^
536 (f==null ? 0 : f.hashCode());
537 result = prime * result + fieldHashCode;
540 result = prime * result + matches;
545 public boolean equals(Object obj) {
552 if (getClass() != obj.getClass()) {
555 Match other = (Match) obj;
556 if (fields == null) {
557 if (other.fields != null) {
560 } else if (!fields.equals(other.fields)) {
563 if (matches != other.matches) {
570 public String toString() {
571 StringBuilder builder = new StringBuilder();
572 builder.append("Match [fields=");
573 builder.append(fields);
574 builder.append(", matches=");
575 builder.append(matches);
577 return builder.toString();