X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fsal%2Fapi%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Fmatch%2FMatch.java;h=2c3cfb8303736229e6c86b618642f6bd72b12aaa;hp=00a2f57308d57e1d76c8f0e88aeb86d74a3f910a;hb=81754b97dad4d38ee9a38516664f6b1b8c99d48c;hpb=541d0a36997f292bb037a2199463431eee538358 diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java index 00a2f57308..2c3cfb8303 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/Match.java @@ -202,6 +202,7 @@ public class Match implements Cloneable, Serializable { Match cloned = null; try { cloned = (Match) super.clone(); + cloned.matches = matches; cloned.fields = new HashMap(); for (Entry entry : this.fields.entrySet()) { cloned.fields.put(entry.getKey(), entry.getValue().clone()); @@ -225,13 +226,14 @@ public class Match implements Cloneable, Serializable { Match reverse = this.clone(); // Flip symmetric fields - for (Map.Entry entry : Match.reversableMatches - .entrySet()) { + for (Map.Entry entry : Match.reversableMatches.entrySet()) { MatchType from = entry.getKey(); MatchType to = entry.getValue(); if (this.isPresent(from)) { - reverse.setField(to, this.getField(from).getValue(), this - .getField(from).getMask()); + reverse.setField(to, this.getField(from).getValue(), this.getField(from).getMask()); + if (!this.isPresent(to)) { + reverse.clearField(from); + } } } @@ -244,125 +246,250 @@ public class Match implements Cloneable, Serializable { /** * Check whether the current match conflicts with the passed filter match * This match conflicts with the filter if for at least a MatchType defined - * in the filter match, the respective MatchFields differ or are not compatible + * in the filter match, the respective MatchFields differ or are not + * compatible + * + * In other words the function returns true if the set of packets described + * by one match and the set of packets described by the other match are + * disjoint. Equivalently, if the intersection of the two sets of packets + * described by the two matches is an empty. * * For example, Let's suppose the filter has the following MatchFields: * DL_TYPE = 0x800 - * NW_DST = 172.20.30.110/24 + * NW_DST = 172.20.30.110/24 * * while this match has the following MatchFields: + * DL_TYPE = 0x800 * NW_DST = 172.20.30.45/24 * TP_DST = 80 * - * Then the function would return false as the two Match are not conflicting + * Then the function would return false as the two Match are not + * conflicting. * - * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST + * Note: the mask value is taken into account only for MatchType.NW_SRC and + * MatchType.NW_DST * - * @param match the MAtch describing the filter - * @return true if the match is conflicting with the filter, false otherwise + * @param match + * the Match describing the filter + * @return true if the set of packets described by one match and the set of + * packets described by the other match are disjoint, false + * otherwise */ public boolean conflictWithFilter(Match filter) { - // Iterate through the MatchType defined in the filter - for (MatchType type : filter.getMatchesList()) { + return !this.intersetcs(filter); + } + + /** + * Merge the current Match fields with the fields of the filter Match. A + * check is first run to see if this Match is compatible with the filter + * Match. If it is not, the merge is not attempted. + * + * The result is the match object representing the intersection of the set + * of packets described by this match with the set of packets described by + * the filter match. If the intersection of the two sets is empty, the + * return match will be null. + * + * @param filter + * the match with which attempting the merge + * @return a new Match object describing the set of packets represented by + * the intersection of this and the filter matches. null if the + * intersection is empty. + */ + public Match mergeWithFilter(Match filter) { + return this.getIntersection(filter); + } + + /** + * Return the match representing the intersection of the set of packets + * described by this match with the set of packets described by the other + * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and + * m.getIntersection(o) == o where u is an empty match (universal set, all + * packets) and o is the null match (empty set). + * + * @param other + * the match with which computing the intersection + * @return a new Match object representing the intersection of the set of + * packets described by this match with the set of packets described + * by the other match. null when the intersection is the empty set. + */ + public Match getIntersection(Match other) { + // If no intersection, return the empty set + if (!this.intersetcs(other)) { + return null; + } + // Check if any of the two is the universal match + if (this.getMatches() == 0) { + return other.clone(); + } + if (other.getMatches() == 0) { + return this.clone(); + } + // Derive the intersection + Match intersection = new Match(); + for (MatchType type : MatchType.values()) { + if (this.isAny(type) && other.isAny(type)) { + continue; + } if (this.isAny(type)) { + intersection.setField(other.getField(type).clone()); + continue; + } else if (other.isAny(type)) { + intersection.setField(this.getField(type).clone()); + continue; + } + // Either they are equal or it is about IP address + switch (type) { + // When it is about IP address, take the wider prefix address + // between the twos + case NW_SRC: + case NW_DST: + MatchField thisField = this.getField(type); + MatchField otherField = other.getField(type); + InetAddress thisAddress = (InetAddress) thisField.getValue(); + InetAddress otherAddress = (InetAddress) otherField.getValue(); + InetAddress thisMask = (InetAddress) thisField.getMask(); + InetAddress otherMask = (InetAddress) otherField.getMask(); + + int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(thisMask); + int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(otherMask); + if (thisMaskLen < otherMaskLen) { + intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen), + otherMask)); + } else { + intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen), + thisMask)); + } + break; + default: + // this and other match field are equal for this type, pick this + // match field + intersection.setField(this.getField(type).clone()); + } + } + return intersection; + } + + /** + * Checks whether the intersection of the set of packets described by this + * match with the set of packets described by the other match is non empty + * + * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22 + * + * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4 + * + * then their respective matching packets set intersection is non empty: + * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4 + * + * @param other + * the other match with which testing the intersection + * @return true if the intersection of the respective matching packets sets + * is non empty + */ + public boolean intersetcs(Match other) { + // No intersection with the empty set + if (other == null) { + return false; + } + // Always intersection with the universal set + if (this.getMatches() == 0 || other.getMatches() == 0) { + return true; + } + // Iterate through the MatchType defined in the filter + for (MatchType type : MatchType.values()) { + if (this.isAny(type) || other.isAny(type)) { continue; } MatchField thisField = this.getField(type); - MatchField filterField = filter.getField(type); + MatchField otherField = other.getField(type); switch (type) { case DL_SRC: case DL_DST: - if (Arrays.equals((byte[]) thisField.getValue(), - (byte[]) filterField.getValue())) { + if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) { return false; } break; case NW_SRC: case NW_DST: InetAddress thisAddress = (InetAddress) thisField.getValue(); - InetAddress filterAddress = (InetAddress) filterField - .getValue(); + InetAddress otherAddress = (InetAddress) otherField.getValue(); // Validity check - if (thisAddress instanceof Inet4Address - && filterAddress instanceof Inet6Address - || thisAddress instanceof Inet6Address - && filterAddress instanceof Inet4Address) { - return true; + if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address + || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) { + return false; } InetAddress thisMask = (InetAddress) thisField.getMask(); - InetAddress filterMask = (InetAddress) filterField.getMask(); - // thisAddress has to be in same subnet of filterAddress - if (NetUtils.inetAddressConflict(thisAddress, filterAddress, - thisMask, filterMask)) { - return true; + InetAddress otherMask = (InetAddress) otherField.getMask(); + if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask) + && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) { + return false; } break; default: - if (!thisField.getValue().equals(filterField.getValue())) { - return true; - } - } - //TODO: check v4 v6 incompatibility - } - return false; - } - - /** - * Merge the current Match fields with the fields of the filter Match - * A check is first run to see if this Match is compatible with the - * filter Match. If it is not, the merge is not attempted. - * - * - * @param filter - * @return - */ - public Match mergeWithFilter(Match filter) { - if (!this.conflictWithFilter(filter)) { - /* - * No conflict with the filter - * We can copy over the fields which this match does not have - */ - for (MatchType type : filter.getMatchesList()) { - if (this.isAny(type)) { - this.setField(filter.getField(type).clone()); + if (!thisField.getValue().equals(otherField.getValue())) { + return false; } } } - return this; + return true; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((fields == null) ? 0 : fields.hashCode()); + if (this.fields == null) { + result = prime * result; + } else { + int sum = 0; + for (MatchType field : this.fields.keySet()) { + MatchField f = this.fields.get(field); + sum = sum + ((field==null ? 0 : field.calculateConsistentHashCode()) ^ + (f==null ? 0 : f.hashCode())); + } + result = prime * result + sum; + } result = prime * result + matches; return result; } @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } Match other = (Match) obj; if (fields == null) { - if (other.fields != null) + if (other.fields != null) { return false; - } else if (!fields.equals(other.fields)) + } + } else if (!fields.equals(other.fields)) { return false; - if (matches != other.matches) + } + if (matches != other.matches) { return false; + } return true; } @Override public String toString() { - return "Match[" + fields.values() + "]"; + StringBuilder builder = new StringBuilder(); + builder.append("Match [fields="); + builder.append(fields); + builder.append(", matches="); + builder.append(matches); + builder.append("]"); + return builder.toString(); } + }