3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.sal.match;
12 import java.io.Serializable;
13 import java.net.Inet4Address;
14 import java.net.Inet6Address;
15 import java.net.InetAddress;
16 import java.util.ArrayList;
17 import java.util.Arrays;
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;
25 import javax.xml.bind.annotation.XmlAccessType;
26 import javax.xml.bind.annotation.XmlAccessorType;
27 import javax.xml.bind.annotation.XmlElement;
28 import javax.xml.bind.annotation.XmlRootElement;
30 import org.opendaylight.controller.sal.utils.EtherTypes;
31 import org.opendaylight.controller.sal.utils.IPProtocols;
32 import org.opendaylight.controller.sal.utils.NetUtils;
35 * Represents the generic match criteria for a network frame/packet/message
36 * It contains a collection of individual field match
40 @XmlAccessorType(XmlAccessType.NONE)
41 public class Match implements Cloneable, Serializable {
42 private static final long serialVersionUID = 1L;
43 private static final Map<MatchType, MatchType> reversableMatches;
45 Map<MatchType, MatchType> map = new HashMap<MatchType, MatchType>();
46 map.put(MatchType.DL_SRC, MatchType.DL_DST);
47 map.put(MatchType.DL_DST, MatchType.DL_SRC);
48 map.put(MatchType.NW_SRC, MatchType.NW_DST);
49 map.put(MatchType.NW_DST, MatchType.NW_SRC);
50 map.put(MatchType.TP_SRC, MatchType.TP_DST);
51 map.put(MatchType.TP_DST, MatchType.TP_SRC);
52 reversableMatches = Collections.unmodifiableMap(map);
54 private Map<MatchType, MatchField> fields;
55 private int matches; // concise way to tell which fields the match is set for (may remove if not needed)
58 fields = new HashMap<MatchType, MatchField>();
62 public Match(Match match) {
63 fields = new HashMap<MatchType, MatchField>(match.fields);
64 matches = match.matches;
68 * Generic setter for frame/packet/message's header fields against which to match
69 * Note: For MAC addresses, please pass the cloned value to this function
71 * @param type packet's header field type
72 * @param value field's value to assign to the match
73 * @param mask field's bitmask to apply to the match (has to be of the same class type of value)
75 public void setField(MatchType type, Object value, Object mask) {
76 MatchField field = new MatchField(type, value, mask);
77 if (field.isValid()) {
78 fields.put(type, field);
79 matches |= type.getIndex();
84 * Generic setter for frame/packet/message's header fields against which to match
85 * Note: For MAC addresses, please pass the cloned value to this function
87 * @param type packet's header field type
88 * @param value field's value to assign to the match
90 public void setField(MatchType type, Object value) {
91 MatchField field = new MatchField(type, value);
92 if (field.isValid()) {
93 fields.put(type, field);
94 matches |= type.getIndex();
99 * Generic setter for frame/packet/message's header field against which to match
101 * @param field the fields parameters as MAtchField object
103 public void setField(MatchField field) {
104 if (field.isValid()) {
105 fields.put(field.getType(), field);
106 matches |= field.getType().getIndex();
111 * Generic method to clear a field from the match
113 public void clearField(MatchType type) {
115 matches &= ~type.getIndex();
119 * Generic getter for fields against which the match is programmed
121 * @param type frame/packet/message's header field type
124 public MatchField getField(MatchType type) {
125 return fields.get(type);
129 * Returns the fields the match is set for in a bitmask fashion
130 * Each bit represents a field the match is configured for
132 * @return the 32 bit long mask (Refer to {@code}org.opendaylight.controller.sal.match.MatchElement)
134 public int getMatches() {
139 * Returns the list of MatchType fields the match is set for
141 * @return List of individual MatchType fields.
143 public List<MatchType> getMatchesList() {
144 return new ArrayList<MatchType>(fields.keySet());
148 * Returns the list of MatchFields the match is set for
150 * @return List of individual MatchField values.
152 @XmlElement(name="matchField")
153 public List<MatchField> getMatchFields() {
154 return new ArrayList<MatchField>(fields.values());
158 * Returns whether this match is for an IPv6 flow
160 public boolean isIPv6() {
161 return (isPresent(MatchType.DL_TYPE)
162 && ((Short) getField(MatchType.DL_TYPE).getValue())
163 .equals(EtherTypes.IPv6.shortValue())
164 || isPresent(MatchType.NW_PROTO)
165 && ((Byte) getField(MatchType.NW_PROTO).getValue())
166 .equals(IPProtocols.IPV6ICMP.byteValue())
167 || isPresent(MatchType.NW_SRC)
168 && getField(MatchType.NW_SRC).getValue() instanceof Inet6Address || isPresent(MatchType.NW_DST)
169 && getField(MatchType.NW_DST).getValue() instanceof Inet6Address);
173 * Returns whether this match is for an IPv4 flow
175 public boolean isIPv4() {
180 * Returns whether for the specified field type the match is to be considered "any"
181 * Equivalent to say this match does not care about the value of the specified field
186 public boolean isAny(MatchType type) {
187 //return ((fields.get(type) == null) || (fields.get(type).getBitMask() == 0L));
188 return !fields.containsKey(type);
192 * Returns whether a match for the specified field type is configured
197 public boolean isPresent(MatchType type) {
198 return (fields.get(type) != null);
202 public Match clone() {
205 cloned = (Match) super.clone();
206 cloned.matches = matches;
207 cloned.fields = new HashMap<MatchType, MatchField>();
208 for (Entry<MatchType, MatchField> entry : this.fields.entrySet()) {
209 cloned.fields.put(entry.getKey(), entry.getValue().clone());
211 } catch (CloneNotSupportedException e) {
212 throw new RuntimeException(e);
218 * Returns a reversed version of this match
219 * For example, in the reversed version the network source and destination
220 * addresses will be exchanged. Non symmetric match field will not be
221 * copied over into the reversed match version, like input port.
225 public Match reverse() {
226 // Copy over all fields
227 Match reverse = this.clone();
229 // Flip symmetric fields
230 for (Map.Entry<MatchType, MatchType> entry : Match.reversableMatches.entrySet()) {
231 MatchType from = entry.getKey();
232 MatchType to = entry.getValue();
233 if (this.isPresent(from)) {
234 reverse.setField(to, this.getField(from).getValue(), this.getField(from).getMask());
235 if (!this.isPresent(to)) {
236 reverse.clearField(from);
241 // Reset asymmetric fields
242 reverse.clearField(MatchType.IN_PORT);
248 * Check whether the current match conflicts with the passed filter match
249 * This match conflicts with the filter if for at least a MatchType defined
250 * in the filter match, the respective MatchFields differ or are not
253 * In other words the function returns true if the set of packets described
254 * by one match and the set of packets described by the other match are
255 * disjoint. Equivalently, if the intersection of the two sets of packets
256 * described by the two matches is an empty.
258 * For example, Let's suppose the filter has the following MatchFields:
260 * NW_DST = 172.20.30.110/24
262 * while this match has the following MatchFields:
264 * NW_DST = 172.20.30.45/24
267 * Then the function would return false as the two Match are not
270 * Note: the mask value is taken into account only for MatchType.NW_SRC and
274 * the Match describing the filter
275 * @return true if the set of packets described by one match and the set of
276 * packets described by the other match are disjoint, false
279 public boolean conflictWithFilter(Match filter) {
280 return !this.intersetcs(filter);
284 * Merge the current Match fields with the fields of the filter Match. A
285 * check is first run to see if this Match is compatible with the filter
286 * Match. If it is not, the merge is not attempted.
288 * The result is the match object representing the intersection of the set
289 * of packets described by this match with the set of packets described by
290 * the filter match. If the intersection of the two sets is empty, the
291 * return match will be null.
294 * the match with which attempting the merge
295 * @return a new Match object describing the set of packets represented by
296 * the intersection of this and the filter matches. null if the
297 * intersection is empty.
299 public Match mergeWithFilter(Match filter) {
300 return this.getIntersection(filter);
304 * Return the match representing the intersection of the set of packets
305 * described by this match with the set of packets described by the other
306 * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
307 * m.getIntersection(o) == o where u is an empty match (universal set, all
308 * packets) and o is the null match (empty set).
311 * the match with which computing the intersection
312 * @return a new Match object representing the intersection of the set of
313 * packets described by this match with the set of packets described
314 * by the other match. null when the intersection is the empty set.
316 public Match getIntersection(Match other) {
317 // If no intersection, return the empty set
318 if (!this.intersetcs(other)) {
321 // Check if any of the two is the universal match
322 if (this.getMatches() == 0) {
323 return other.clone();
325 if (other.getMatches() == 0) {
328 // Derive the intersection
329 Match intersection = new Match();
330 for (MatchType type : MatchType.values()) {
331 if (this.isAny(type) && other.isAny(type)) {
334 if (this.isAny(type)) {
335 intersection.setField(other.getField(type).clone());
337 } else if (other.isAny(type)) {
338 intersection.setField(this.getField(type).clone());
341 // Either they are equal or it is about IP address
343 // When it is about IP address, take the wider prefix address
347 MatchField thisField = this.getField(type);
348 MatchField otherField = other.getField(type);
349 InetAddress thisAddress = (InetAddress) thisField.getValue();
350 InetAddress otherAddress = (InetAddress) otherField.getValue();
351 InetAddress thisMask = (InetAddress) thisField.getMask();
352 InetAddress otherMask = (InetAddress) otherField.getMask();
354 int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
355 .getSubnetMaskLength(thisMask);
356 int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
357 .getSubnetMaskLength(otherMask);
358 if (thisMaskLen < otherMaskLen) {
359 intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen),
362 intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen),
367 // this and other match field are equal for this type, pick this
369 intersection.setField(this.getField(type).clone());
376 * Checks whether the intersection of the set of packets described by this
377 * match with the set of packets described by the other match is non empty
379 * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
381 * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
383 * then their respective matching packets set intersection is non empty:
384 * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
387 * the other match with which testing the intersection
388 * @return true if the intersection of the respective matching packets sets
391 public boolean intersetcs(Match other) {
392 // No intersection with the empty set
396 // Always intersection with the universal set
397 if (this.getMatches() == 0 || other.getMatches() == 0) {
400 // Iterate through the MatchType defined in the filter
401 for (MatchType type : MatchType.values()) {
402 if (this.isAny(type) || other.isAny(type)) {
406 MatchField thisField = this.getField(type);
407 MatchField otherField = other.getField(type);
412 if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
418 InetAddress thisAddress = (InetAddress) thisField.getValue();
419 InetAddress otherAddress = (InetAddress) otherField.getValue();
421 if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
422 || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
425 InetAddress thisMask = (InetAddress) thisField.getMask();
426 InetAddress otherMask = (InetAddress) otherField.getMask();
427 if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
428 && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
433 if (!thisField.getValue().equals(otherField.getValue())) {
442 public int hashCode() {
443 final int prime = 31;
445 if (this.fields == null) {
446 result = prime * result;
448 // use a tree map as the order of hashMap is not guaranteed.
449 // 2 Match objects with fields in different order are still equal.
450 // Hence the hashCode should be the same too.
451 TreeMap<MatchType, MatchField> tm = new TreeMap<MatchType, MatchField>(this.fields);
452 for (MatchType field : tm.keySet()) {
453 MatchField f = tm.get(field);
454 int fieldHashCode = (field==null ? 0 : field.calculateConsistentHashCode()) ^
455 (f==null ? 0 : f.hashCode());
456 result = prime * result + fieldHashCode;
459 result = prime * result + matches;
464 public boolean equals(Object obj) {
471 if (getClass() != obj.getClass()) {
474 Match other = (Match) obj;
475 if (fields == null) {
476 if (other.fields != null) {
479 } else if (!fields.equals(other.fields)) {
482 if (matches != other.matches) {
489 public String toString() {
490 StringBuilder builder = new StringBuilder();
491 builder.append("Match [fields=");
492 builder.append(fields);
493 builder.append(", matches=");
494 builder.append(matches);
496 return builder.toString();