From 2c4b7c792b049ece3177b8b6fc6b6331038c5f5b Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Tue, 10 Dec 2013 09:42:10 -0800 Subject: [PATCH 1/1] Initial commit for new Match classes - Existing match classes rely on a static enum of match types. This change is to allow extensibility for new matching fields. - This commit introduces new classes without replacing old ones and without migrating any code to use the new ones yet. Change-Id: I76531c10ae480123044ec1023bce8750ce7906ba Signed-off-by: Alessandro Boch --- .../sal/match/extensible/DlDst.java | 106 ++++ .../sal/match/extensible/DlSrc.java | 106 ++++ .../sal/match/extensible/DlType.java | 105 ++++ .../sal/match/extensible/DlVlan.java | 104 ++++ .../sal/match/extensible/DlVlanPriority.java | 105 ++++ .../sal/match/extensible/InPort.java | 108 ++++ .../sal/match/extensible/Match.java | 422 +++++++++++++ .../sal/match/extensible/MatchField.java | 128 ++++ .../sal/match/extensible/NwDst.java | 131 ++++ .../sal/match/extensible/NwProtocol.java | 116 ++++ .../sal/match/extensible/NwSrc.java | 131 ++++ .../sal/match/extensible/NwTos.java | 115 ++++ .../sal/match/extensible/TpDst.java | 104 ++++ .../sal/match/extensible/TpSrc.java | 104 ++++ .../sal/match/MatchExtensibleTest.java | 589 ++++++++++++++++++ 15 files changed, 2474 insertions(+) create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java create mode 100644 opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java new file mode 100644 index 0000000000..381de8e23a --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlDst.java @@ -0,0 +1,106 @@ +package org.opendaylight.controller.sal.match.extensible; + +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.HexEncode; +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class DlDst extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "DL_DST"; + private byte[] address; + + /** + * Creates a Match field for the destination data layer address + * + * @param address + * the data layer address. The constructor makes a copy of it + */ + public DlDst(byte[] address) { + super(TYPE); + if (address != null) { + this.address = Arrays.copyOf(address, address.length); + } + } + + // To satisfy JAXB + public DlDst() { + super(TYPE); + } + + @Override + public byte[] getValue() { + return Arrays.copyOf(address, address.length); + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return HexEncode.bytesToHexStringFormat(address); + } + + @Override + public byte[] getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return address != null && address.length == NetUtils.MACAddrLengthInBytes; + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public DlSrc getReverse() { + return new DlSrc(address); + } + + @Override + public DlDst clone() { + return new DlDst(address); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(address); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DlDst)) { + return false; + } + DlDst other = (DlDst) obj; + return Arrays.equals(address, other.address); + } +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java new file mode 100644 index 0000000000..962c4d38e9 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlSrc.java @@ -0,0 +1,106 @@ +package org.opendaylight.controller.sal.match.extensible; + +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.HexEncode; +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class DlSrc extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "DL_SRC"; + private byte[] address; + + /** + * Creates a Match field for the source datalayer address + * + * @param address + * the datalayer address. The constructor makes a copy of it + */ + public DlSrc(byte[] address) { + super(TYPE); + if (address != null) { + this.address = Arrays.copyOf(address, address.length); + } + } + + // To satisfy JAXB + private DlSrc() { + super(TYPE); + } + + @Override + public byte[] getValue() { + return Arrays.copyOf(address, address.length); + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return HexEncode.bytesToHexStringFormat(address); + } + + @Override + public byte[] getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return address != null && address.length == NetUtils.MACAddrLengthInBytes; + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public DlDst getReverse() { + return new DlDst(address); + } + + @Override + public DlSrc clone() { + return new DlSrc(address); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(address); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DlSrc)) { + return false; + } + DlSrc other = (DlSrc) obj; + return Arrays.equals(address, other.address); + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java new file mode 100644 index 0000000000..468703d552 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlType.java @@ -0,0 +1,105 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.EtherTypes; +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class DlType extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "DL_TYPE"; + private short ethertype; + + /** + * Creates a Match field for the data layer type + * + * @param address + * the data layer type + */ + public DlType(short ethertype) { + super(TYPE); + this.ethertype = ethertype; + } + + // To satisfy JAXB + private DlType() { + super(TYPE); + } + + @Override + public Short getValue() { + return ethertype; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedShort(ethertype))); + } + + @Override + public Short getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public DlType getReverse() { + return this.clone(); + } + + @Override + public DlType clone() { + return new DlType(ethertype); + } + + @Override + public boolean isV6() { + return this.ethertype == EtherTypes.IPv6.shortValue(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ethertype; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DlType)) { + return false; + } + DlType other = (DlType) obj; + if (ethertype != other.ethertype) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java new file mode 100644 index 0000000000..30657a90bf --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlan.java @@ -0,0 +1,104 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class DlVlan extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "DL_VLAN"; + private static final short MAX = 4095; + private short vlan; + + /** + * Creates a Match field for the data layer type + * + * @param address + * the data layer type + */ + public DlVlan(short vlan) { + super(TYPE); + this.vlan = vlan; + } + + // To satisfy JAXB + private DlVlan() { + super(TYPE); + } + + @Override + public Short getValue() { + return vlan; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.valueOf(vlan); + } + + @Override + public Short getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return vlan >= 0 && vlan <= MAX; + } + + @Override + public DlVlan getReverse() { + return this.clone(); + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public DlVlan clone() { + return new DlVlan(vlan); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + vlan; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DlVlan)) { + return false; + } + DlVlan other = (DlVlan) obj; + if (vlan != other.vlan) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java new file mode 100644 index 0000000000..58dd5639a2 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/DlVlanPriority.java @@ -0,0 +1,105 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class DlVlanPriority extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "DL_VLAN_PR"; + private static final byte MAX = 7; + private byte vlanPriority; + + /** + * Creates a Match field for the data layer type + * + * @param address + * the data layer type + */ + public DlVlanPriority(byte vlanPriority) { + super(TYPE); + this.vlanPriority = vlanPriority; + } + + // To satisfy JAXB + private DlVlanPriority() { + super(TYPE); + } + + @Override + public Byte getValue() { + return vlanPriority; + } + + @Override + @XmlElement(name = "mask") + protected String getValueString() { + return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(vlanPriority))); + } + + @Override + public Byte getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return vlanPriority >= 0 && vlanPriority <= MAX; + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public DlVlanPriority getReverse() { + return this.clone(); + } + + @Override + public DlVlanPriority clone() { + return new DlVlanPriority(vlanPriority); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + vlanPriority; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DlVlanPriority)) { + return false; + } + DlVlanPriority other = (DlVlanPriority) obj; + if (vlanPriority != other.vlanPriority) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java new file mode 100644 index 0000000000..2b5eb5b574 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/InPort.java @@ -0,0 +1,108 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.core.NodeConnector; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class InPort extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "IN_PORT"; + private NodeConnector port; + + /** + * Creates a Match field for the input port + * + * @param port + * the input port + */ + public InPort(NodeConnector port) { + super(TYPE); + this.port = port; + } + + // To satisfy JAXB + private InPort() { + super(TYPE); + } + + @Override + public NodeConnector getValue() { + return port; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return port.toString(); + } + + @Override + public NodeConnector getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public InPort getReverse() { + return this.clone(); + } + + @Override + public InPort clone() { + return new InPort(port); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((port == null) ? 0 : port.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof InPort)) { + return false; + } + InPort other = (InPort) obj; + if (port == null) { + if (other.port != null) { + return false; + } + } else if (!port.equals(other.port)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java new file mode 100644 index 0000000000..b065444c53 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/Match.java @@ -0,0 +1,422 @@ + +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.match.extensible; + +import java.io.Serializable; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + + +import org.opendaylight.controller.sal.utils.NetUtils; + +/** + * Represents the generic match criteria for a network frame/packet/message + * It contains a collection of individual field match + * + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class Match implements Cloneable, Serializable { + private static final long serialVersionUID = 1L; + private Map> fields; + + public Match() { + fields = new HashMap>(); + } + + public Match(Match match) { + fields = new HashMap>(match.fields); + } + + /** + * Generic setter for frame/packet/message's header field against which to match + * + * @param field the fields parameters as MAtchField object + */ + public void setField(MatchField field) { + if (field.isValid()) { + fields.put(field.getType(), field); + } + } + + /** + * Generic method to clear a field from the match + */ + public void clearField(String type) { + fields.remove(type); + } + + /** + * Generic getter for fields against which the match is programmed + * + * @param type frame/packet/message's header field type + * @return + */ + public MatchField getField(String type) { + return fields.get(type); + } + + /** + * Returns the list of MatchType fields the match is set for + * + * @return List of individual MatchType fields. + */ + public List getMatchesList() { + return new ArrayList(fields.keySet()); + } + + /** + * Returns the list of MatchFields the match is set for + * + * @return List of individual MatchField values. + */ + @XmlElement(name="matchField") + public List> getMatchFields() { + return new ArrayList>(fields.values()); + } + + /** + * Returns whether this match is for an IPv6 flow + */ + public boolean isIPv6() { + if (isPresent(DlType.TYPE)) { + for (MatchField field : fields.values()) { + if (!field.isV6()) { + return false; + } + } + } + return true; + } + + /** + * Returns whether this match is for an IPv4 flow + */ + public boolean isIPv4() { + return !isIPv6(); + } + + /** + * Returns whether for the specified field type the match is to be considered "any" + * Equivalent to say this match does not care about the value of the specified field + * + * @param type + * @return + */ + public boolean isAny(String type) { + return !fields.containsKey(type); + } + + /** + * Returns whether a match for the specified field type is configured + * + * @param type + * @return + */ + public boolean isPresent(String type) { + return (fields.get(type) != null); + } + + public boolean isEmpty() { + return fields.isEmpty(); + } + + @Override + public Match clone() { + Match cloned = null; + try { + cloned = (Match) super.clone(); + cloned.fields = new HashMap>(); + for (Entry> entry : this.fields.entrySet()) { + cloned.fields.put(entry.getKey(), entry.getValue().clone()); + } + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + return cloned; + } + + /** + * Returns a reversed version of this match + * For example, in the reversed version the network source and destination + * addresses will be exchanged. Non symmetric match field will not be + * copied over into the reversed match version, like input port. + * + * @return + */ + public Match reverse() { + Match reverse = new Match(); + for (MatchField field : fields.values()) { + reverse.setField(field.hasReverse()? field.getReverse() : field.clone()); + } + + // Reset asymmetric fields + reverse.clearField(InPort.TYPE); + + return reverse; + } + + /** + * 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 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 org.opendaylight.controller.sal.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 + * + * 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. + * + * 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 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) { + 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 org.opendaylight.controller.sal.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.isEmpty()) { + return other.clone(); + } + if (other.isEmpty()) { + return this.clone(); + } + // Get all the match types for both filters + Set allTypes = new HashSet(this.fields.keySet()); + allTypes.addAll(new HashSet(other.fields.keySet())); + // Derive the intersection + Match intersection = new Match(); + for (String type : allTypes) { + 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 NwSrc.TYPE: + case NwDst.TYPE: + 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); + + InetAddress subnetPrefix = null; + InetAddress subnetMask = null; + if (thisMaskLen < otherMaskLen) { + subnetPrefix = NetUtils.getSubnetPrefix(otherAddress, otherMaskLen); + subnetMask = otherMask; + } else { + subnetPrefix = NetUtils.getSubnetPrefix(thisAddress, thisMaskLen); + subnetMask = thisMask; + } + MatchField field = (type.equals(NwSrc.TYPE)) ? new NwSrc(subnetPrefix, subnetMask) : new NwDst( + subnetPrefix, subnetMask); + intersection.setField(field); + 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.isEmpty() || other.isEmpty()) { + return true; + } + + // Get all the match types for both filters + Set allTypes = new HashSet(this.fields.keySet()); + allTypes.addAll(new HashSet(other.fields.keySet())); + + // Iterate through all the match types defined in the two filters + for (String type : allTypes) { + if (this.isAny(type) || other.isAny(type)) { + continue; + } + + MatchField thisField = this.getField(type); + MatchField otherField = other.getField(type); + + switch (type) { + case DlSrc.TYPE: + case DlDst.TYPE: + if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) { + return false; + } + break; + case NwSrc.TYPE: + case NwDst.TYPE: + InetAddress thisAddress = (InetAddress) thisField.getValue(); + InetAddress otherAddress = (InetAddress) otherField.getValue(); + // Validity check + if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address + || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) { + return false; + } + InetAddress thisMask = (InetAddress) thisField.getMask(); + 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(otherField.getValue())) { + return false; + } + } + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fields == null) ? 0 : fields.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Match)) { + return false; + } + Match other = (Match) obj; + if (fields == null) { + if (other.fields != null) { + return false; + } + } else if (!fields.equals(other.fields)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Match[" + fields.values() + "]"; + } +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java new file mode 100644 index 0000000000..e7a5de3850 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/MatchField.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.match.extensible; + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Represents the generic matching field object + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public abstract class MatchField implements Cloneable, Serializable { + private static final long serialVersionUID = 1L; + private String type; + + // To satisfy JAXB + @SuppressWarnings("unused") + private MatchField() { + } + + public MatchField(String type) { + this.type = type; + } + + @XmlElement(name = "type") + public String getType() { + return type; + } + + /** + * Returns the value set for this match field + * + * @return + */ + public abstract T getValue(); + + @XmlElement(name = "value") + protected abstract String getValueString(); + + /** + * Returns the mask value set for this field match A null mask means this is + * a full match + * + * @return + */ + public abstract T getMask(); + + @XmlElement(name = "mask") + protected abstract String getMaskString(); + + /** + * Returns whether the field match configuration is valid or not + * + * @return true if valid, false otherwise + */ + public abstract boolean isValid(); + + public abstract boolean hasReverse(); + + /** + * Returns the reverse match field. For example for a MatchField matching on + * source ip 1.1.1.1 it will return a MatchField matching on destination IP + * 1.1.1.1. For not reversable MatchField, a copy of this MatchField will be + * returned + * + * @return the correspondent reverse MatchField object or a copy of this + * object if the field is not reversable + */ + public abstract MatchField getReverse(); + + /** + * Returns whether the match field is congruent with IPv6 frames + * + * @return true if congruent with IPv6 frames + */ + public abstract boolean isV6(); + + @Override + public abstract MatchField clone(); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MatchField)) { + return false; + } + MatchField other = (MatchField) obj; + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + return true; + } + + @Override + public String toString() { + return (getMask() == null) ? String.format("%s(%s)", getType(), getValueString()) : + String.format("%s(%s,%s)", getType(), getValueString(), getMaskString()); + } + +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java new file mode 100644 index 0000000000..42b6ba6cc0 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwDst.java @@ -0,0 +1,131 @@ +package org.opendaylight.controller.sal.match.extensible; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class NwDst extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "NW_DST"; + private InetAddress address; + private InetAddress mask; + + /** + * Creates a Match field for the network destination address + * + * @param address + * the network address + * @param mask + * the network mask + */ + public NwDst(InetAddress address, InetAddress mask) { + super(TYPE); + this.address = address; + this.mask = mask; + } + + // To satisfy JAXB + private NwDst() { + super(TYPE); + } + + public NwDst(InetAddress address) { + super(TYPE); + this.address = address; + this.mask = null; + } + + @Override + public InetAddress getValue() { + return address; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return address.getHostAddress(); + } + + @Override + public InetAddress getMask() { + return mask; + } + + @Override + @XmlElement(name = "mask") + protected String getMaskString() { + return (mask == null) ? "null" : mask.getHostAddress(); + } + + @Override + public boolean isValid() { + if (address != null) { + if (mask != null) { + return address instanceof Inet4Address && mask instanceof Inet4Address + || address instanceof Inet6Address && mask instanceof Inet6Address; + } + return true; + } + return false; + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public NwSrc getReverse() { + return new NwSrc(address, mask); + } + + @Override + public NwDst clone() { + return new NwDst(address, mask); + } + + @Override + public boolean isV6() { + return address instanceof Inet6Address; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((mask == null) ? 0 : mask.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof NwDst)) { + return false; + } + NwDst other = (NwDst) obj; + // Equality to be checked against prefix addresses + int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(this.mask); + int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(other.mask); + + return NetUtils.getSubnetPrefix(address, thisMaskLen).equals( + NetUtils.getSubnetPrefix(other.address, otherMaskLen)); + } +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java new file mode 100644 index 0000000000..c5b5315902 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwProtocol.java @@ -0,0 +1,116 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class NwProtocol extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "NW_PROTO"; + private static final short MAX = 255; + private byte protocol; + + /** + * Creates a Match field for the network protocol + * + * @param protocol + * the protocol number + */ + public NwProtocol(byte protocol) { + super(TYPE); + this.protocol = protocol; + } + + public NwProtocol(int protocol) { + super(TYPE); + this.protocol = (byte) protocol; + } + + public NwProtocol(short protocol) { + super(TYPE); + this.protocol = (byte) protocol; + } + + // To satisfy JAXB + private NwProtocol() { + super(TYPE); + } + + @Override + public Byte getValue() { + return protocol; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(protocol))); + } + + @Override + public Byte getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + int intProtocol = NetUtils.getUnsignedByte(protocol); + return intProtocol >= 0 && intProtocol <= MAX; + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public NwProtocol getReverse() { + return this.clone(); + } + + @Override + public NwProtocol clone() { + return new NwProtocol(protocol); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + protocol; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof NwProtocol)) { + return false; + } + NwProtocol other = (NwProtocol) obj; + if (protocol != other.protocol) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java new file mode 100644 index 0000000000..4da43f53a0 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwSrc.java @@ -0,0 +1,131 @@ +package org.opendaylight.controller.sal.match.extensible; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class NwSrc extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "NW_SRC"; + private InetAddress address; + private InetAddress mask; + + /** + * Creates a Match field for the network source address + * + * @param address + * the network address + * @param mask + * the network mask + */ + public NwSrc(InetAddress address, InetAddress mask) { + super(TYPE); + this.address = address; + this.mask = mask; + } + + // To satisfy JAXB + private NwSrc() { + super(TYPE); + } + + public NwSrc(InetAddress address) { + super(TYPE); + this.address = address; + this.mask = null; + } + + @Override + public InetAddress getValue() { + return address; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return address.toString(); + } + + @Override + public InetAddress getMask() { + return mask; + } + + @Override + @XmlElement(name = "mask") + protected String getMaskString() { + return mask == null ? "null" : mask.toString(); + } + + @Override + public boolean isValid() { + if (address != null) { + if (mask != null) { + return address instanceof Inet4Address && mask instanceof Inet4Address + || address instanceof Inet6Address && mask instanceof Inet6Address; + } + return true; + } + return false; + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public NwDst getReverse() { + return new NwDst(address, mask); + } + + @Override + public NwSrc clone() { + return new NwSrc(address, mask); + } + + @Override + public boolean isV6() { + return address instanceof Inet6Address; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((mask == null) ? 0 : mask.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof NwSrc)) { + return false; + } + NwSrc other = (NwSrc) obj; + // Equality to be checked against prefix addresses + int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(this.mask); + int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils + .getSubnetMaskLength(other.mask); + + return NetUtils.getSubnetPrefix(address, thisMaskLen).equals( + NetUtils.getSubnetPrefix(other.address, otherMaskLen)); + } +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java new file mode 100644 index 0000000000..ba5b562bd2 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/NwTos.java @@ -0,0 +1,115 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class NwTos extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "NW_TOS"; + private static final short MAX = 63; + private byte tos; + + /** + * Creates a Match field for the network TOS + * + * @param address + * the network TOS + */ + public NwTos(byte tos) { + super(TYPE); + this.tos = tos; + } + + public NwTos(int tos) { + super(TYPE); + this.tos = (byte) tos; + } + + public NwTos(short tos) { + super(TYPE); + this.tos = (byte) tos; + } + + // To satisfy JAXB + private NwTos() { + super(TYPE); + } + + @Override + public Byte getValue() { + return tos; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(tos))); + } + + @Override + public Byte getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return tos >= 0 && tos <= MAX; + } + + @Override + public boolean hasReverse() { + return false; + } + + @Override + public NwTos getReverse() { + return this.clone(); + } + + @Override + public NwTos clone() { + return new NwTos(tos); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + tos; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof NwTos)) { + return false; + } + NwTos other = (NwTos) obj; + if (tos != other.tos) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java new file mode 100644 index 0000000000..022db4cc17 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpDst.java @@ -0,0 +1,104 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class TpDst extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "TP_DST"; + private short port; + + /** + * Creates a Match field for the transport destination port + * + * @param port + * the transport port + */ + public TpDst(short port) { + super(TYPE); + this.port = port; + } + + // To satisfy JAXB + private TpDst() { + super(TYPE); + } + + @Override + public Short getValue() { + return port; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.valueOf(NetUtils.getUnsignedShort(port)); + } + + @Override + public Short getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public TpSrc getReverse() { + return new TpSrc(port); + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public TpDst clone() { + return new TpDst(port); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + port; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof TpDst)) { + return false; + } + TpDst other = (TpDst) obj; + if (port != other.port) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java new file mode 100644 index 0000000000..1c93d1e847 --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/match/extensible/TpSrc.java @@ -0,0 +1,104 @@ +package org.opendaylight.controller.sal.match.extensible; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.opendaylight.controller.sal.utils.NetUtils; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class TpSrc extends MatchField { + private static final long serialVersionUID = 1L; + public static final String TYPE = "TP_SRC"; + private short port; + + /** + * Creates a Match field for the Transport source port + * + * @param port + * the transport port + */ + public TpSrc(short port) { + super(TYPE); + this.port = port; + } + + // To satisfy JAXB + private TpSrc() { + super(TYPE); + } + + @Override + public Short getValue() { + return port; + } + + @Override + @XmlElement(name = "value") + protected String getValueString() { + return String.valueOf(NetUtils.getUnsignedShort(port)); + } + + @Override + public Short getMask() { + return null; + } + + @Override + protected String getMaskString() { + return null; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public boolean hasReverse() { + return true; + } + + @Override + public TpDst getReverse() { + return new TpDst(port); + } + + @Override + public TpSrc clone() { + return new TpSrc(port); + } + + @Override + public boolean isV6() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + port; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof TpSrc)) { + return false; + } + TpSrc other = (TpSrc) obj; + if (port != other.port) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java new file mode 100644 index 0000000000..0f49f421d2 --- /dev/null +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/match/MatchExtensibleTest.java @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.match; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + + +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.controller.sal.core.Node; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.match.extensible.DlDst; +import org.opendaylight.controller.sal.match.extensible.DlSrc; +import org.opendaylight.controller.sal.match.extensible.DlType; +import org.opendaylight.controller.sal.match.extensible.DlVlan; +import org.opendaylight.controller.sal.match.extensible.DlVlanPriority; +import org.opendaylight.controller.sal.match.extensible.InPort; +import org.opendaylight.controller.sal.match.extensible.Match; +import org.opendaylight.controller.sal.match.extensible.MatchField; +import org.opendaylight.controller.sal.match.extensible.NwDst; +import org.opendaylight.controller.sal.match.extensible.NwProtocol; +import org.opendaylight.controller.sal.match.extensible.NwSrc; +import org.opendaylight.controller.sal.match.extensible.NwTos; +import org.opendaylight.controller.sal.match.extensible.TpDst; +import org.opendaylight.controller.sal.match.extensible.TpSrc; +import org.opendaylight.controller.sal.utils.EtherTypes; +import org.opendaylight.controller.sal.utils.IPProtocols; +import org.opendaylight.controller.sal.utils.NodeConnectorCreator; +import org.opendaylight.controller.sal.utils.NodeCreator; + +public class MatchExtensibleTest { + @Test + public void testMatchCreation() { + Node node = NodeCreator.createOFNode(7L); + NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 6, node); + MatchField field = new InPort(port); + + Assert.assertTrue(field != null); + Assert.assertEquals(field.getType(), InPort.TYPE); + Assert.assertEquals(field.getValue(), port); + Assert.assertTrue(field.isValid()); + + + byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 }; + field = null; + field = new DlSrc(mac); + Assert.assertNotNull(field.getValue()); + + field = null; + field = new NwTos((byte) 0x22); + Assert.assertNotNull(field.getValue()); + } + + @Test + public void testMatchSetGet() { + Match x = new Match(); + short val = 2346; + NodeConnector inPort = NodeConnectorCreator.createOFNodeConnector(val, NodeCreator.createOFNode(1L)); + x.setField(new InPort(inPort)); + Assert.assertEquals(x.getField(InPort.TYPE).getValue(), inPort); + Assert.assertTrue((Short) ((NodeConnector) x.getField(InPort.TYPE).getValue()).getID() == val); + } + + @Test + public void testMatchSetGetMAC() { + Match x = new Match(); + byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 }; + byte mac2[] = { (byte) 0xaa, (byte) 0xbb, 0, 0, 0, (byte) 0xbb }; + + x.setField(new DlSrc(mac)); + x.setField(new DlDst(mac2)); + Assert.assertArrayEquals(mac, (byte[]) x.getField(DlSrc.TYPE).getValue()); + Assert.assertFalse(Arrays.equals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE) + .getValue())); + + x.setField(new DlDst(mac.clone())); + Assert.assertArrayEquals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE).getValue()); + } + + @Test + public void testMatchSetGetNWAddr() throws UnknownHostException { + Match x = new Match(); + String ip = "172.20.231.23"; + InetAddress address = InetAddress.getByName(ip); + InetAddress mask = InetAddress.getByName("255.255.0.0"); + + x.setField(new NwSrc(address, mask)); + Assert.assertEquals(address, x.getField(NwSrc.TYPE).getValue()); + Assert.assertEquals(x.getField(NwSrc.TYPE).getMask(), mask); + } + + @Test + public void testMatchSetGetEtherType() throws UnknownHostException { + Match x = new Match(); + + x.setField(new DlType(EtherTypes.QINQ.shortValue())); + Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.QINQ.shortValue()); + + x.setField(new DlType(EtherTypes.LLDP.shortValue())); + Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.LLDP.shortValue()); + Assert.assertFalse(x.getField(DlType.TYPE).equals(EtherTypes.LLDP.intValue())); + } + + @Test + public void testSetGetNwTos() { + Match x = new Match(); + x.setField(new NwTos((byte) 0xb)); + + Byte t = new Byte((byte) 0xb); + + Object o = x.getField(NwTos.TYPE).getValue(); + Assert.assertEquals(o, t); + Assert.assertEquals(o, Byte.valueOf((byte)0xb)); + } + + @Test + public void testSetGetNwProto() { + Match x = new Match(); + Byte proto = (byte) 199; + x.setField(new NwProtocol(proto)); + + Byte o = (Byte) x.getField(NwProtocol.TYPE).getValue(); + Assert.assertEquals(o, proto); + } + + @Test + public void testSetTpSrc() { + // Minimum value validation. + Match match = new Match(); + short tp_src = 0; + match.setField(new TpSrc(tp_src)); + + Object o = match.getField(TpSrc.TYPE).getValue(); + Assert.assertEquals(o, tp_src); + + // Maximum value validation. + match = new Match(); + tp_src = (short) 0xffff; + match.setField(new TpSrc(tp_src)); + + o = match.getField(TpSrc.TYPE).getValue(); + Assert.assertEquals(o, tp_src); + } + + @Test + public void testSetTpDst() { + // Minimum value validation. + Match match = new Match(); + short tp_dst = 0; + match.setField(new TpDst(tp_dst)); + + Object o = match.getField(TpDst.TYPE).getValue(); + Assert.assertTrue(o.equals(tp_dst)); + + // Maximum value validation. + match = new Match(); + tp_dst = (short) 0xffff; + match.setField(new TpDst(tp_dst)); + + o = match.getField(TpDst.TYPE).getValue(); + Assert.assertEquals(o, tp_dst); + } + + @Test + public void testEquality() throws Exception { + Node node = NodeCreator.createOFNode(7L); + NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node); + NodeConnector port2 = NodeConnectorCreator.createOFNodeConnector((short) 24, node); + byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355"); + InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0"); + InetAddress ipMask = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0"); + InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"); + InetAddress ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0"); + InetAddress ipMaskd2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"); + short ethertype = EtherTypes.IPv6.shortValue(); + short ethertype2 = EtherTypes.IPv6.shortValue(); + short vlan = (short) 27, vlan2 = (short) 27; + byte vlanPr = (byte) 3, vlanPr2 = (byte) 3; + Byte tos = 4, tos2 = 4; + byte proto = IPProtocols.UDP.byteValue(), proto2 = IPProtocols.UDP.byteValue(); + short src = (short) 5500, src2 = (short) 5500; + short dst = 80, dst2 = 80; + + /* + * Create a SAL Flow aFlow + */ + Match match1 = new Match(); + Match match2 = new Match(); + match1.setField(new InPort(port)); + match1.setField(new DlSrc(srcMac)); + match1.setField(new DlDst(dstMac)); + match1.setField(new DlType(ethertype)); + match1.setField(new DlVlan(vlan)); + match1.setField(new DlVlanPriority(vlanPr)); + match1.setField(new NwSrc(srcIP, ipMask)); + match1.setField(new NwDst(dstIP, ipMaskd)); + match1.setField(new NwTos(tos)); + match1.setField(new NwProtocol(proto)); + match1.setField(new TpSrc(src)); + match1.setField(new TpDst(dst)); + + match2.setField(new InPort(port2)); + match2.setField(new DlSrc(srcMac2)); + match2.setField(new DlDst(dstMac2)); + match2.setField(new DlType(ethertype2)); + match2.setField(new DlVlan(vlan2)); + match2.setField(new DlVlanPriority(vlanPr2)); + match2.setField(new NwSrc(srcIP, ipMask2)); + match2.setField(new NwDst(dstIP, ipMaskd2)); + match2.setField(new NwTos(tos2)); + match2.setField(new NwProtocol(proto2)); + match2.setField(new TpSrc(src2)); + match2.setField(new TpDst(dst2)); + + Assert.assertTrue(match1.equals(match2)); + + Set allFields = new HashSet(match1.getMatchesList()); + allFields.addAll(match2.getMatchesList()); + // Make sure all values are equals + for (String type : allFields) { + if (match1.isPresent(type)) { + Assert.assertEquals(match1.getField(type), match2.getField(type)); + } + } + + // Make none of the fields couples are pointing to the same reference + MatchField a = null, b = null; + for (String type : allFields) { + a = match1.getField(type); + b = match2.getField(type); + if (a != null && b != null) { + Assert.assertFalse(a == b); + } + } + } + + @Test + public void testEqualityNetMask() throws Exception { + + InetAddress srcIP = InetAddress.getByName("1.1.1.1"); + InetAddress ipMask = InetAddress.getByName("255.255.255.255"); + InetAddress srcIP2 = InetAddress.getByName("1.1.1.1"); + InetAddress ipMask2 = null; + short ethertype = EtherTypes.IPv4.shortValue(); + short ethertype2 = EtherTypes.IPv4.shortValue(); + + /* + * Create a SAL Flow aFlow + */ + Match match1 = new Match(); + Match match2 = new Match(); + + match1.setField(new DlType(ethertype)); + match1.setField(new NwSrc(srcIP, ipMask)); + + match2.setField(new DlType(ethertype2)); + match2.setField(new NwSrc(srcIP2, ipMask2)); + + Assert.assertTrue(match1.equals(match2)); + + ipMask2 = InetAddress.getByName("255.255.255.255"); + match2.setField(new NwSrc(srcIP2, ipMask2)); + + srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355"); + srcIP2 = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355"); + ipMask = null; + ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + ethertype = EtherTypes.IPv6.shortValue(); + ethertype2 = EtherTypes.IPv6.shortValue(); + + match1.setField(new DlType(ethertype)); + match1.setField(new NwSrc(srcIP, ipMask)); + + match2.setField(new DlType(ethertype2)); + match2.setField(new NwSrc(srcIP2, ipMask2)); + + Assert.assertEquals(match1, match2); + } + + @Test + public void testHashCodeWithReverseMatch() throws Exception { + InetAddress srcIP1 = InetAddress.getByName("1.1.1.1"); + InetAddress ipMask1 = InetAddress.getByName("255.255.255.255"); + InetAddress srcIP2 = InetAddress.getByName("2.2.2.2"); + InetAddress ipMask2 = InetAddress.getByName("255.255.255.255"); + MatchField field1 = new NwSrc(srcIP1, ipMask1); + MatchField field2 = new NwDst(srcIP2, ipMask2); + Match match1 = new Match(); + match1.setField(field1); + match1.setField(field2); + Match match2 = match1.reverse(); + Assert.assertFalse(match1.hashCode() == match2.hashCode()); + } + + @Test + public void testHashCode() throws Exception { + byte srcMac1[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte dstMac1[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + short ethertype = EtherTypes.IPv4.shortValue(); + short ethertype2 = EtherTypes.IPv4.shortValue(); + InetAddress srcIP1 = InetAddress.getByName("1.1.1.1"); + InetAddress ipMask1 = InetAddress.getByName("255.255.255.255"); + InetAddress srcIP2 = InetAddress.getByName("1.1.1.1"); + InetAddress ipMask2 = InetAddress.getByName("255.255.255.255"); + + Match match1 = new Match(); + Match match2 = new Match(); + + MatchField field1 = new DlSrc(srcMac1); + MatchField field2 = new DlSrc(srcMac2); + Assert.assertTrue(field1.hashCode() == field2.hashCode()); + + match1.setField(field1); + match2.setField(field2); + Assert.assertTrue(match1.hashCode() == match2.hashCode()); + + MatchField field3 = new DlDst(dstMac1); + MatchField field4 = new DlDst(dstMac2); + Assert.assertTrue(field3.hashCode() == field4.hashCode()); + + match1.setField(field3); + match2.setField(field4); + Assert.assertTrue(match1.hashCode() == match2.hashCode()); + + MatchField field5 = new DlType(ethertype); + MatchField field6 = new DlType(ethertype2); + Assert.assertTrue(field5.hashCode() == field6.hashCode()); + + match1.setField(field5); + match2.setField(field6); + Assert.assertTrue(match1.hashCode() == match2 .hashCode()); + + MatchField field7 = new NwSrc(srcIP1, ipMask1); + MatchField field8 = new NwSrc(srcIP2, ipMask2); + Assert.assertTrue(field7.hashCode() == field8.hashCode()); + + match1.setField(field7); + match2.setField(field8); + Assert.assertTrue(match1.hashCode() == match2.hashCode()); + + } + + @Test + public void testCloning() throws Exception { + Node node = NodeCreator.createOFNode(7L); + NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node); + byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355"); + InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0"); + InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0"); + InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"); + short ethertype = EtherTypes.IPv6.shortValue(); + short vlan = (short) 27; + byte vlanPr = (byte) 3; + Byte tos = 4; + byte proto = IPProtocols.UDP.byteValue(); + short src = (short) 5500; + short dst = 80; + + /* + * Create a SAL Flow aFlow + */ + Match match = new Match(); + match.setField(new InPort(port)); + match.setField(new DlSrc(srcMac)); + match.setField(new DlDst(dstMac)); + match.setField(new DlType(ethertype)); + match.setField(new DlVlan(vlan)); + match.setField(new DlVlanPriority(vlanPr)); + match.setField(new NwSrc(srcIP, ipMasks)); + match.setField(new NwDst(dstIP, ipMaskd)); + match.setField(new NwTos(tos)); + match.setField(new NwProtocol(proto)); + match.setField(new TpSrc(src)); + match.setField(new TpDst(dst)); + + Match cloned = match.clone(); + + // Make sure all values are equals + for (String type : match.getMatchesList()) { + if (match.isPresent(type)) { + if (!match.getField(type).equals(cloned.getField(type))) { + Assert.assertEquals(match.getField(type), cloned.getField(type)); + } + } + } + + // Make sure none of the fields couples are pointing to the same + // reference + MatchField a = null, b = null; + for (String type : match.getMatchesList()) { + a = match.getField(type); + b = cloned.getField(type); + if (a != null && b != null) { + Assert.assertFalse(a == b); + } + } + + Assert.assertTrue(match.equals(cloned)); + + Assert.assertEquals(match.getField(DlSrc.TYPE), cloned.getField(DlSrc.TYPE)); + Assert.assertEquals(match.getField(NwDst.TYPE), cloned.getField(NwDst.TYPE)); + Assert.assertEquals(match.getField(NwDst.TYPE).getMask(), cloned.getField(NwDst.TYPE).getMask()); + Assert.assertEquals(match.hashCode(), cloned.hashCode()); + } + + @Test + public void testFlip() throws Exception { + Node node = NodeCreator.createOFNode(7L); + NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node); + byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc }; + byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f }; + InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355"); + InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0"); + InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0"); + InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0"); + short ethertype = EtherTypes.IPv6.shortValue(); + short vlan = (short) 27; + byte vlanPr = (byte) 3; + Byte tos = 4; + byte proto = IPProtocols.UDP.byteValue(); + short src = (short) 5500; + short dst = 80; + + /* + * Create a SAL Flow aFlow + */ + Match match = new Match(); + match.setField(new InPort(port)); + match.setField(new DlSrc(srcMac)); + match.setField(new DlDst(dstMac)); + match.setField(new DlType(ethertype)); + match.setField(new DlVlan(vlan)); + match.setField(new DlVlanPriority(vlanPr)); + match.setField(new NwSrc(srcIP, ipMasks)); + match.setField(new NwDst(dstIP, ipMaskd)); + match.setField(new NwTos(tos)); + match.setField(new NwProtocol(proto)); + match.setField(new TpSrc(src)); + match.setField(new TpDst(dst)); + + Match flipped = match.reverse(); + + Assert.assertEquals(match.getField(DlType.TYPE), flipped.getField(DlType.TYPE)); + Assert.assertEquals(match.getField(DlVlan.TYPE), flipped.getField(DlVlan.TYPE)); + + Assert.assertArrayEquals((byte[]) match.getField(DlDst.TYPE).getValue(), (byte[]) flipped.getField(DlSrc.TYPE) + .getValue()); + + Assert.assertEquals(match.getField(NwDst.TYPE).getValue(), flipped.getField(NwSrc.TYPE).getValue()); + + Assert.assertEquals(match.getField(TpDst.TYPE).getValue(), flipped.getField(TpSrc.TYPE).getValue()); + + Match flipflip = flipped.reverse().reverse(); + Assert.assertEquals(flipflip, flipped); + + } + + @Test + public void testVlanNone() throws Exception { + // The value 0 is used to indicate that no VLAN ID is set + short vlan = (short) 0; + MatchField field = new DlVlan(vlan); + + Assert.assertTrue(field != null); + Assert.assertEquals(field.getValue(), new Short(vlan)); + Assert.assertTrue(field.isValid()); + } + + @Test + public void testIntersection() throws UnknownHostException { + Short ethType = Short.valueOf((short)0x800); + InetAddress ip1 = InetAddress.getByName("1.1.1.1"); + InetAddress ip2 = InetAddress.getByName("1.1.1.0"); + InetAddress ipm2 = InetAddress.getByName("255.255.255.0"); + InetAddress ip3 = InetAddress.getByName("1.3.0.0"); + InetAddress ipm3 = InetAddress.getByName("255.255.0.0"); + InetAddress ip4 = InetAddress.getByName("1.3.4.4"); + InetAddress ipm4 = InetAddress.getByName("255.255.255.0"); + + Match m1 = new Match(); + m1.setField(new DlType(ethType)); + m1.setField(new NwSrc(ip1)); + + Match m2 = new Match(); + m2.setField(new DlType(ethType)); + m2.setField(new NwSrc(ip2, ipm2)); + + Match m3 = new Match(); + m3.setField(new DlType(ethType)); + m3.setField(new NwSrc(ip3, ipm3)); + m3.setField(new NwProtocol(IPProtocols.TCP.byteValue())); + + Match m3r = m3.reverse(); + Assert.assertTrue(m3.intersetcs(m3r)); + + Assert.assertTrue(m1.intersetcs(m2)); + Assert.assertTrue(m2.intersetcs(m1)); + Assert.assertFalse(m1.intersetcs(m3)); + Assert.assertTrue(m1.intersetcs(m3r)); + Assert.assertFalse(m3.intersetcs(m1)); + Assert.assertTrue(m3.intersetcs(m1.reverse())); + Assert.assertFalse(m2.intersetcs(m3)); + Assert.assertFalse(m3.intersetcs(m2)); + Assert.assertTrue(m2.intersetcs(m3r)); + + + Match i = m1.getIntersection(m2); + Assert.assertTrue(((Short)i.getField(DlType.TYPE).getValue()).equals(ethType)); + // Verify intersection of IP addresses is correct + Assert.assertTrue(((InetAddress)i.getField(NwSrc.TYPE).getValue()).equals(ip1)); + Assert.assertNull(i.getField(NwSrc.TYPE).getMask()); + + // Empty set + i = m2.getIntersection(m3); + Assert.assertNull(i); + + Match m4 = new Match(); + m4.setField(new DlType(ethType)); + m4.setField(new NwProtocol(IPProtocols.TCP.byteValue())); + m3.setField(new NwSrc(ip4, ipm4)); + Assert.assertTrue(m4.intersetcs(m3)); + + // Verify intersection of IP and IP mask addresses is correct + Match ii = m3.getIntersection(m4); + Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getValue()).equals(ip4)); + Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getMask()).equals(ipm4)); + + Match m5 = new Match(); + m5.setField(new DlType(ethType)); + m3.setField(new NwSrc(ip3, ipm3)); + m5.setField(new NwProtocol(IPProtocols.UDP.byteValue())); + Assert.assertFalse(m5.intersetcs(m3)); + Assert.assertFalse(m5.intersetcs(m4)); + Assert.assertTrue(m5.intersetcs(m5)); + Assert.assertFalse(m3.intersetcs(m5)); + Assert.assertFalse(m4.intersetcs(m5)); + + + Match i2 = m4.getIntersection(m3); + Assert.assertFalse(i2.isEmpty()); + Assert.assertFalse(i2.getMatchesList().isEmpty()); + Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getValue()).equals(ip3)); + Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getMask()).equals(ipm3)); + Assert.assertTrue(((Byte)i2.getField(NwProtocol.TYPE).getValue()).equals(IPProtocols.TCP.byteValue())); + + byte src[] = {(byte)0, (byte)0xab,(byte)0xbc,(byte)0xcd,(byte)0xde,(byte)0xef}; + byte dst[] = {(byte)0x10, (byte)0x11,(byte)0x12,(byte)0x13,(byte)0x14,(byte)0x15}; + Short srcPort = (short)1024; + Short dstPort = (short)80; + + // Check identity + Match m6 = new Match(); + m6.setField(new DlSrc(src)); + m6.setField(new DlDst(dst)); + m6.setField(new NwSrc(ip2, ipm2)); + m6.setField(new NwDst(ip3, ipm3)); + m6.setField(new NwProtocol(IPProtocols.UDP.byteValue())); + m6.setField(new TpSrc(srcPort)); + m6.setField(new TpDst(dstPort)); + Assert.assertTrue(m6.intersetcs(m6)); + Assert.assertTrue(m6.getIntersection(m6).equals(m6)); + + // Empty match, represents the universal set (all packets) + Match u = new Match(); + Assert.assertEquals(m6.getIntersection(u), m6); + Assert.assertEquals(u.getIntersection(m6), m6); + + // No intersection with null match, empty set + Assert.assertNull(m6.getIntersection(null)); + } +} -- 2.36.6