--- /dev/null
+/*
+ * Copyright (c) 2013, 2015 IBM Corporation 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.openflowplugin.impl.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Provides comparator for comparing according to various {@link Match} attributes
+ *
+ */
+public final class MatchComparatorFactory {
+
+ private MatchComparatorFactory() {
+ // NOOP
+ }
+
+ private static final Collection<SimpleComparator<Match>> MATCH_COMPARATORS = new ArrayList<>();
+ static {
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createEthernet());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createIcmpv4());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createInPhyPort());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createInPort());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createIp());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createL3());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createL4());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createProtocolMatchFields());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createMetadata());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createNull());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createTunnel());
+ MATCH_COMPARATORS.add(MatchComparatorFactory.createVlan());
+ }
+
+ public static SimpleComparator<Match> createNull() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by whole object
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ return (statsMatch == null) == (storedMatch == null);
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createVlan() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by VLAN
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getVlanMatch() == null) {
+ if (statsMatch.getVlanMatch() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getVlanMatch().equals(statsMatch.getVlanMatch())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createTunnel() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by tunnel
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getTunnel() == null) {
+ if (statsMatch.getTunnel() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getTunnel().equals(statsMatch.getTunnel())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createProtocolMatchFields() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by protocol fields
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getProtocolMatchFields() == null) {
+ if (statsMatch.getProtocolMatchFields() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getProtocolMatchFields().equals(statsMatch.getProtocolMatchFields())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createMetadata() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by metadata
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getMetadata() == null) {
+ if (statsMatch.getMetadata() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getMetadata().equals(statsMatch.getMetadata())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createL4() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by layer4
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getLayer4Match() == null) {
+ if (statsMatch.getLayer4Match() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getLayer4Match().equals(statsMatch.getLayer4Match())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createL3() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by layer3
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getLayer3Match() == null) {
+ if (statsMatch.getLayer3Match() != null) {
+ return false;
+ }
+ } else if (!MatchComparatorHelper.layer3MatchEquals(statsMatch.getLayer3Match(), storedMatch.getLayer3Match())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createIp() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by Ip
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getIpMatch() == null) {
+ if (statsMatch.getIpMatch() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getIpMatch().equals(statsMatch.getIpMatch())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createInPort() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by InPort
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getInPort() == null) {
+ if (statsMatch.getInPort() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getInPort().equals(statsMatch.getInPort())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createInPhyPort() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by InPhyPort
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getInPhyPort() == null) {
+ if (statsMatch.getInPhyPort() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getInPhyPort().equals(statsMatch.getInPhyPort())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createEthernet() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by Ethernet
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getEthernetMatch() == null) {
+ if (statsMatch.getEthernetMatch() != null) {
+ return false;
+ }
+ } else if (!MatchComparatorHelper.ethernetMatchEquals(statsMatch.getEthernetMatch(), storedMatch.getEthernetMatch())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createIcmpv4() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Comparation by Icmpv4
+ */
+ @Override
+ public boolean areObjectsEqual(Match statsMatch, Match storedMatch) {
+ if (storedMatch == null) {
+ return false;
+ }
+ if (storedMatch.getIcmpv4Match() == null) {
+ if (statsMatch.getIcmpv4Match() != null) {
+ return false;
+ }
+ } else if (!storedMatch.getIcmpv4Match().equals(statsMatch.getIcmpv4Match())) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ public static SimpleComparator<Match> createMatch() {
+ return new SimpleComparator<Match>() {
+ /**
+ * Compares flows by whole match
+ */
+ @Override
+ public boolean areObjectsEqual(final Match statsFlow, final Match storedFlow) {
+ if (statsFlow == null) {
+ if (storedFlow != null) {
+ return false;
+ }
+ } else if (!compareMatches(statsFlow, storedFlow)) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+
+ /**
+ * Explicit equals method to compare the 'match' for flows stored in the data-stores and flow fetched from the switch.
+ * Flow installation process has three steps
+ * 1) Store flow in config data store
+ * 2) and send it to plugin for installation
+ * 3) Flow gets installed in switch
+ *
+ * The flow user wants to install and what finally gets installed in switch can be slightly different.
+ * E.g, If user installs flow with src/dst ip=10.0.0.1/24, when it get installed in the switch
+ * src/dst ip will be changes to 10.0.0.0/24 because of netmask of 24. When statistics manager fetch
+ * stats it gets 10.0.0.0/24 rather then 10.0.0.1/24. Custom match takes care of by using masked ip
+ * while comparing two ip addresses.
+ *
+ * Sometimes when user don't provide few values that is required by flow installation request, like
+ * priority,hard timeout, idle timeout, cookies etc, plugin usages default values before sending
+ * request to the switch. So when statistics manager gets flow statistics, it gets the default value.
+ * But the flow stored in config data store don't have those defaults value. I included those checks
+ * in the customer flow/match equal function.
+ *
+ *
+ * @param statsMatch
+ * @param storedMatch
+ * @return
+ */
+ private static boolean compareMatches(final Match statsMatch, final Match storedMatch) {
+ if (statsMatch == storedMatch) {
+ return true;
+ }
+
+ for (SimpleComparator<Match> matchComp : MATCH_COMPARATORS) {
+ if (!matchComp.areObjectsEqual(statsMatch, storedMatch)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.openflowplugin.impl.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedBytes;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchArbitraryBitMask;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchArbitraryBitMask;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.opendaylight.ipv6.arbitrary.bitmask.fields.rev160224.Ipv6ArbitraryMask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @author joe
+ * @author sai.marapareddy@gmail.com
+ *
+ */
+public class MatchComparatorHelper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MatchComparatorHelper.class);
+ private static final int DEFAULT_SUBNET = 32;
+ private static final int IPV4_MASK_LENGTH = 32;
+ private static final int SHIFT_OCTET_1 = 24;
+ private static final int SHIFT_OCTET_2 = 16;
+ private static final int SHIFT_OCTET_3 = 8;
+ private static final int SHIFT_OCTET_4 = 0;
+ private static final int POSITION_OCTET_1 = 0;
+ private static final int POSITION_OCTET_2 = 1;
+ private static final int POSITION_OCTET_3 = 2;
+ private static final int POSITION_OCTET_4 = 3;
+ private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255";
+ private static final String DEFAULT_IPV6_ARBITRARY_BIT_MASK = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
+ private static final String PREFIX_SEPARATOR = "/";
+ private static final int IPV4_ADDRESS_LENGTH = 32;
+ private static final int IPV6_ADDRESS_LENGTH = 128;
+ private static final int BYTE_SIZE = 8;
+
+ /*
+ * Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
+ * (upper or lower or mix). Ethernet Match which controller receives from switch is always an upper case string.
+ * Default EthernetMatch equals doesn't use equalsIgnoreCase() and hence it fails. E.g User provided mac address
+ * string in flow match is aa:bb:cc:dd:ee:ff and when controller fetch statistic data, openflow driver library
+ * returns AA:BB:CC:DD:EE:FF and default eqauls fails here.
+ */
+ @VisibleForTesting
+ static boolean ethernetMatchEquals(final EthernetMatch statsEthernetMatch, final EthernetMatch storedEthernetMatch) {
+ boolean verdict = true;
+ final Boolean checkNullValues = checkNullValues(statsEthernetMatch, storedEthernetMatch);
+ if (checkNullValues != null) {
+ verdict = checkNullValues;
+ } else {
+ verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetSource(),
+ storedEthernetMatch.getEthernetSource());
+ if (verdict) {
+ verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetDestination(),
+ storedEthernetMatch.getEthernetDestination());
+ }
+ if (verdict) {
+ if (statsEthernetMatch.getEthernetType() == null) {
+ if (storedEthernetMatch.getEthernetType() != null) {
+ verdict = false;
+ }
+ } else {
+ verdict = statsEthernetMatch.getEthernetType().equals(storedEthernetMatch.getEthernetType());
+ }
+ }
+ }
+ return verdict;
+ }
+
+ static boolean ethernetMatchFieldsEquals(final MacAddressFilter statsEthernetMatchFields,
+ final MacAddressFilter storedEthernetMatchFields) {
+ boolean verdict = true;
+ final Boolean checkNullValues = checkNullValues(statsEthernetMatchFields, storedEthernetMatchFields);
+ if (checkNullValues != null) {
+ verdict = checkNullValues;
+ } else {
+ verdict = macAddressEquals(statsEthernetMatchFields.getAddress(), storedEthernetMatchFields.getAddress());
+ if (verdict) {
+ verdict = macAddressEquals(statsEthernetMatchFields.getMask(), storedEthernetMatchFields.getMask());
+ }
+ }
+ return verdict;
+ }
+
+ static boolean macAddressEquals(final MacAddress statsMacAddress, final MacAddress storedMacAddress) {
+ boolean verdict = true;
+ final Boolean checkNullValues = checkNullValues(statsMacAddress, storedMacAddress);
+ if (checkNullValues != null) {
+ verdict = checkNullValues;
+ } else {
+ verdict = statsMacAddress.getValue().equalsIgnoreCase(storedMacAddress.getValue());
+ }
+ return verdict;
+ }
+
+ @VisibleForTesting
+ static boolean layer3MatchEquals(final Layer3Match statsLayer3Match, final Layer3Match storedLayer3Match) {
+ boolean verdict = true;
+ if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match) {
+ final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
+ final Ipv4Match storedIpv4Match = (Ipv4Match) storedLayer3Match;
+ verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(storedIpv4Match.getIpv4Destination(),
+ statsIpv4Match.getIpv4Destination());
+ if (verdict) {
+ verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(statsIpv4Match.getIpv4Source(),
+ storedIpv4Match.getIpv4Source());
+ }
+ } else if (statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6Match) {
+ final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
+ final Ipv6Match storedIpv6Match = (Ipv6Match) storedLayer3Match;
+ verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(storedIpv6Match.getIpv6Destination(),
+ statsIpv6Match.getIpv6Destination());
+ if (verdict) {
+ verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(statsIpv6Match.getIpv6Source(),
+ storedIpv6Match.getIpv6Source());
+ }
+ } else if (statsLayer3Match instanceof Ipv4MatchArbitraryBitMask && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
+ // At this moment storedIpv4MatchArbitraryBitMask & statsIpv4MatchArbitraryBitMask will always have non null arbitrary masks.
+ // In case of no / null arbitrary mask, statsLayer3Match will be an instance of Ipv4Match.
+ // Eg:- stats -> 1.0.1.0/255.0.255.0 stored -> 1.1.1.0/255.0.255.0
+ final Ipv4MatchArbitraryBitMask statsIpv4MatchArbitraryBitMask= (Ipv4MatchArbitraryBitMask) statsLayer3Match;
+ final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
+ if ((storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null |
+ storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null)) {
+ if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
+ String storedDstIpAddress = normalizeIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(),
+ storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
+ String statsDstIpAddress = normalizeIpv4Address(statsIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(),
+ statsIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
+ if (MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue(),
+ statsIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue())) {
+ verdict = MatchComparatorHelper.compareStringNullSafe(storedDstIpAddress,
+ statsDstIpAddress);
+ } else {
+ verdict = false;
+ return verdict;
+ }
+ }
+ if (storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
+ String storedSrcIpAddress = normalizeIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask()
+ ,storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
+ String statsSrcIpAddress = normalizeIpv4Address(statsIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask()
+ ,statsIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
+ if (MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue(),
+ statsIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue())) {
+ verdict = MatchComparatorHelper.compareStringNullSafe(storedSrcIpAddress,
+ statsSrcIpAddress);
+ } else {
+ verdict = false;
+ }
+ }
+ } else {
+ final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
+ if (nullCheckOut != null) {
+ verdict = nullCheckOut;
+ } else {
+ verdict = storedLayer3Match.equals(statsLayer3Match);
+ }
+ }
+ } else if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
+ // Here stored netmask is an instance of Ipv4MatchArbitraryBitMask, when it is pushed in to switch
+ // it automatically converts it in to cidr format in case of certain subnet masks ( consecutive ones or zeroes)
+ // Eg:- stats src/dest -> 1.1.1.0/24 stored src/dest -> 1.1.1.0/255.255.255.0
+ final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
+ final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
+ if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
+ Ipv4Prefix ipv4PrefixDestination;
+ if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask() != null) {
+ byte[] destByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
+ ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(), destByteMask);
+ } else {
+ ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask());
+ }
+ verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixDestination, statsIpv4Match.getIpv4Destination());
+ if (verdict == false) {
+ return verdict;
+ }
+ }
+ if (storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
+ Ipv4Prefix ipv4PrefixSource;
+ if (storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask() != null) {
+ byte[] srcByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
+ ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask(), srcByteMask);
+ } else {
+ ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask());
+ }
+ verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixSource, statsIpv4Match.getIpv4Source());
+ }
+ } else if (statsLayer3Match instanceof Ipv6MatchArbitraryBitMask && storedLayer3Match instanceof Ipv6MatchArbitraryBitMask) {
+ // At this moment storedIpv6MatchArbitraryBitMask & statsIpv6MatchArbitraryBitMask will always have non null arbitrary masks.
+ // In case of no / null arbitrary mask, statsLayer3Match will be an instance of Ipv6Match.
+ // Eg:- stats src/dest -> 2001:2001:2001:2001:2001:2001:2001:2001/FFFF:FFFF:FFFF:FFFF:0000:FFFF:FFFF:FFF0
+ // stored src/dest -> 2001:2001:2001:2001:2001:2001:2001:2001/FFFF:FFFF:FFFF:FFFF:0000:FFFF:FFFF:FFF0
+ final Ipv6MatchArbitraryBitMask statsIpv6MatchArbitraryBitMask= (Ipv6MatchArbitraryBitMask) statsLayer3Match;
+ final Ipv6MatchArbitraryBitMask storedIpv6MatchArbitraryBitMask = (Ipv6MatchArbitraryBitMask) storedLayer3Match;
+ if ((storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask() != null |
+ storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask() != null)) {
+ if (storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask() != null) {
+ String storedDstIpAddress = normalizeIpv6Address(storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask(),
+ storedIpv6MatchArbitraryBitMask.getIpv6DestinationArbitraryBitmask());
+ String statsDstIpAddress = normalizeIpv6Address(statsIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask(),
+ statsIpv6MatchArbitraryBitMask.getIpv6DestinationArbitraryBitmask());
+ String storedDstMask = extractIpv6CanonicalForm(storedIpv6MatchArbitraryBitMask.
+ getIpv6DestinationArbitraryBitmask().getValue()).getHostAddress();
+ String statsDstMask = extractIpv6CanonicalForm(statsIpv6MatchArbitraryBitMask.
+ getIpv6DestinationArbitraryBitmask().getValue()).getHostAddress();
+ if (MatchComparatorHelper.compareStringNullSafe(storedDstMask,statsDstMask)) {
+ verdict = MatchComparatorHelper.compareStringNullSafe(storedDstIpAddress,
+ statsDstIpAddress);
+ } else {
+ verdict = false;
+ return verdict;
+ }
+ }
+ if (storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask() != null) {
+ String storedSrcIpAddress = normalizeIpv6Address(storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask()
+ ,storedIpv6MatchArbitraryBitMask.getIpv6SourceArbitraryBitmask());
+ String statsSrcIpAddress = normalizeIpv6Address(statsIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask()
+ ,statsIpv6MatchArbitraryBitMask.getIpv6SourceArbitraryBitmask());
+ String storedSrcMask = extractIpv6CanonicalForm(storedIpv6MatchArbitraryBitMask.
+ getIpv6SourceArbitraryBitmask().getValue()).getHostAddress();
+ String statsSrcMask = extractIpv6CanonicalForm(statsIpv6MatchArbitraryBitMask.
+ getIpv6SourceArbitraryBitmask().getValue()).getHostAddress();
+ if (MatchComparatorHelper.compareStringNullSafe(storedSrcMask, statsSrcMask)) {
+ verdict = MatchComparatorHelper.compareStringNullSafe(storedSrcIpAddress,
+ statsSrcIpAddress);
+ } else {
+ verdict = false;
+ }
+ }
+ } else {
+ final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
+ if (nullCheckOut != null) {
+ verdict = nullCheckOut;
+ } else {
+ verdict = storedLayer3Match.equals(statsLayer3Match);
+ }
+ }
+ } else if (statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6MatchArbitraryBitMask) {
+ // Here stored netmask is an instance of Ipv6MatchArbitraryBitMask, when it is pushed in to switch
+ // it automatically converts it in to cidr format in case of certain subnet masks ( consecutive ones or zeroes)
+ // Eg:- stats src/dest -> 2001:2001:2001:2001:2001:2001:2001:2001/124
+ // stored src/dest -> 2001:2001:2001:2001:2001:2001:2001:2001/FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFF0
+ final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
+ final Ipv6MatchArbitraryBitMask storedIpv6MatchArbitraryBitMask = (Ipv6MatchArbitraryBitMask) storedLayer3Match;
+ if (storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask() != null) {
+ Ipv6Prefix ipv6PrefixDestination;
+ if (storedIpv6MatchArbitraryBitMask.getIpv6DestinationArbitraryBitmask() != null) {
+ byte[] destByteMask = convertIpv6ArbitraryMaskToByteArray(storedIpv6MatchArbitraryBitMask.getIpv6DestinationArbitraryBitmask());
+ ipv6PrefixDestination = createPrefix(storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask(), destByteMask);
+ } else {
+ ipv6PrefixDestination = createPrefix(storedIpv6MatchArbitraryBitMask.getIpv6DestinationAddressNoMask());
+ }
+ verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(ipv6PrefixDestination, statsIpv6Match.getIpv6Destination());
+ if (verdict == false) {
+ return verdict;
+ }
+ }
+ if (storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask() != null) {
+ Ipv6Prefix ipv6PrefixSource;
+ if (storedIpv6MatchArbitraryBitMask.getIpv6SourceArbitraryBitmask() != null) {
+ byte[] srcByteMask = convertIpv6ArbitraryMaskToByteArray(storedIpv6MatchArbitraryBitMask.getIpv6SourceArbitraryBitmask());
+ ipv6PrefixSource = createPrefix(storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask(), srcByteMask);
+ } else {
+ ipv6PrefixSource = createPrefix(storedIpv6MatchArbitraryBitMask.getIpv6SourceAddressNoMask());
+ }
+ verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(ipv6PrefixSource, statsIpv6Match.getIpv6Source());
+ }
+ } else if (statsLayer3Match instanceof ArpMatch && storedLayer3Match instanceof ArpMatch) {
+ verdict = arpMatchEquals((ArpMatch)statsLayer3Match, (ArpMatch)storedLayer3Match);
+ } else {
+ final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
+ if (nullCheckOut != null) {
+ verdict = nullCheckOut;
+ } else {
+ verdict = storedLayer3Match.equals(statsLayer3Match);
+ }
+ }
+ return verdict;
+ }
+
+ static boolean arpMatchEquals(final ArpMatch statsArpMatch, final ArpMatch storedArpMatch) {
+
+ Integer statsOp = statsArpMatch.getArpOp();
+ Integer storedOp = storedArpMatch.getArpOp();
+
+ Boolean nullCheck = checkNullValues(statsOp, storedOp);
+ if (nullCheck != null) {
+ if (nullCheck == false) {
+ return false;
+ }
+ } else if (!statsOp.equals(storedOp)) {
+ return false;
+ }
+
+ Ipv4Prefix statsIp = statsArpMatch.getArpSourceTransportAddress();
+ Ipv4Prefix storedIp = storedArpMatch.getArpSourceTransportAddress();
+ if (!compareIpv4PrefixNullSafe(statsIp, storedIp)) {
+ return false;
+ }
+
+ statsIp = statsArpMatch.getArpTargetTransportAddress();
+ storedIp = storedArpMatch.getArpTargetTransportAddress();
+ if (!compareIpv4PrefixNullSafe(statsIp, storedIp)) {
+ return false;
+ }
+
+ MacAddressFilter statsMac = statsArpMatch.getArpSourceHardwareAddress();
+ MacAddressFilter storedMac = storedArpMatch.getArpSourceHardwareAddress();
+ if (!ethernetMatchFieldsEquals(statsMac, storedMac)) {
+ return false;
+ }
+
+ statsMac = statsArpMatch.getArpTargetHardwareAddress();
+ storedMac = storedArpMatch.getArpTargetHardwareAddress();
+ if (!ethernetMatchFieldsEquals(statsMac, storedMac)) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * TODO: why don't we use the default Ipv4Prefix.equals()?
+ *
+ * @param statsIpAddress
+ * @param storedIpAddress
+ * @return true if IPv4prefixes equals
+ */
+ static boolean IpAddressEquals(final Ipv4Prefix statsIpAddress, final Ipv4Prefix storedIpAddress) {
+ final IntegerIpAddress statsIpAddressInt = MatchComparatorHelper.strIpToIntIp(statsIpAddress.getValue());
+ final IntegerIpAddress storedIpAddressInt = MatchComparatorHelper.strIpToIntIp(storedIpAddress.getValue());
+
+ if (ipAndMaskBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
+ return true;
+ }
+ if (ipBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
+ return true;
+ }
+ return false;
+ }
+
+ static boolean ipAndMaskBasedMatch(final IntegerIpAddress statsIpAddressInt,
+ final IntegerIpAddress storedIpAddressInt) {
+ return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) == (storedIpAddressInt.getIp() & storedIpAddressInt
+ .getMask()));
+ }
+
+ static boolean ipBasedMatch(final IntegerIpAddress statsIpAddressInt, final IntegerIpAddress storedIpAddressInt) {
+ return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
+ }
+
+
+ private static boolean IpAddressEquals(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
+ final String[] statsIpMask = statsIpv6.getValue().split("/");
+ final String[] storedIpMask = storedIpv6.getValue().split("/");
+ if (! (statsIpMask.length > 1 && storedIpMask.length > 1 && statsIpMask[1].equals(storedIpMask[1]))){
+ return false;
+ }
+
+ final int prefix = Integer.parseInt(statsIpMask[1]);
+ final int byteIndex = prefix/BYTE_SIZE;
+ final int lastByteBits = BYTE_SIZE - (prefix % BYTE_SIZE);
+ final InetAddress statsIp = InetAddresses.forString(statsIpMask[0]);
+ final InetAddress storedIp = InetAddresses.forString(storedIpMask[0]);
+ byte[] statsIpArr = Arrays.copyOfRange(statsIp.getAddress(),0,byteIndex+1);
+ byte[] storedIpArr = Arrays.copyOfRange(storedIp.getAddress(),0,byteIndex+1);
+ statsIpArr[byteIndex] = (byte) (statsIpArr[byteIndex] & (0XFF << lastByteBits));
+ storedIpArr[byteIndex] = (byte) (storedIpArr[byteIndex] & (0XFF << lastByteBits));
+ if(Arrays.equals(statsIpArr,storedIpArr)) {
+ return true;
+ }
+ return false;
+ }
+
+ static Boolean checkNullValues(final Object v1, final Object v2) {
+ Boolean verdict = null;
+ if (v1 == null && v2 != null) {
+ verdict = Boolean.FALSE;
+ } else if (v1 != null && v2 == null) {
+ verdict = Boolean.FALSE;
+ } else if (v1 == null && v2 == null) {
+ verdict = Boolean.TRUE;
+ }
+ return verdict;
+ }
+
+ static boolean compareIpv4PrefixNullSafe(final Ipv4Prefix statsIpv4, final Ipv4Prefix storedIpv4) {
+ boolean verdict = true;
+ final Boolean checkDestNullValuesOut = checkNullValues(storedIpv4, statsIpv4);
+ if (checkDestNullValuesOut != null) {
+ verdict = checkDestNullValuesOut;
+ } else if (!IpAddressEquals(statsIpv4, storedIpv4)) {
+ verdict = false;
+ }
+ return verdict;
+ }
+
+ static boolean compareStringNullSafe(final String stringA, final String stringB) {
+ boolean verdict = true;
+ final Boolean checkDestNullValuesOut = checkNullValues(stringA,stringB);
+ if (checkDestNullValuesOut != null) {
+ verdict = checkDestNullValuesOut;
+ } else if (!stringA.equals(stringB)) {
+ verdict = false;
+ }
+ return verdict;
+ }
+
+ private static boolean compareIpv6PrefixNullSafe(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
+ boolean verdict = true;
+ final Boolean checkDestNullValuesOut = checkNullValues(statsIpv6, storedIpv6);
+ if (checkDestNullValuesOut != null) {
+ verdict = checkDestNullValuesOut;
+ } else if (!IpAddressEquals(statsIpv6, storedIpv6)) {
+ verdict = false;
+ }
+ return verdict;
+ }
+
+ /**
+ * Method return integer version of ip address. Converted int will be mask if mask specified
+ */
+ static IntegerIpAddress strIpToIntIp(final String ipAddresss) {
+
+ final String[] parts = ipAddresss.split("/");
+ final String ip = parts[0];
+ int prefix;
+
+ if (parts.length < 2) {
+ prefix = DEFAULT_SUBNET;
+ } else {
+ prefix = Integer.parseInt(parts[1]);
+ if (prefix < 0 || prefix > IPV4_MASK_LENGTH) {
+ final StringBuilder stringBuilder = new StringBuilder(
+ "Valid values for mask are from range 0 - 32. Value ");
+ stringBuilder.append(prefix);
+ stringBuilder.append(" is invalid.");
+ throw new IllegalStateException(stringBuilder.toString());
+ }
+ }
+
+ IntegerIpAddress integerIpAddress = null;
+
+ final Inet4Address addr = ((Inet4Address) InetAddresses.forString(ip));
+ final byte[] addrBytes = addr.getAddress();
+ // FIXME: what is meaning of anding with 0xFF? Probably could be removed.
+ final int ipInt = ((addrBytes[POSITION_OCTET_1] & 0xFF) << SHIFT_OCTET_1)
+ | ((addrBytes[POSITION_OCTET_2] & 0xFF) << SHIFT_OCTET_2)
+ | ((addrBytes[POSITION_OCTET_3] & 0xFF) << SHIFT_OCTET_3)
+ | ((addrBytes[POSITION_OCTET_4] & 0xFF) << SHIFT_OCTET_4);
+
+ // FIXME: Is this valid?
+ final int mask = 0xffffffff << DEFAULT_SUBNET - prefix;
+
+ integerIpAddress = new IntegerIpAddress(ipInt, mask);
+
+ return integerIpAddress;
+ }
+
+ static boolean isArbitraryBitMask(byte[] byteMask) {
+ if (byteMask == null) {
+ return false;
+ } else {
+ ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>();
+ String maskInBits;
+ // converting byte array to bits
+ maskInBits = new BigInteger(1, byteMask).toString(2);
+ ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)")));
+ for(String string:stringMaskArrayList){
+ integerMaskArrayList.add(Integer.parseInt(string));
+ }
+ return checkArbitraryBitMask(integerMaskArrayList);
+ }
+ }
+
+ static boolean checkArbitraryBitMask(ArrayList<Integer> arrayList) {
+ if (arrayList.size()>0 && arrayList.size()< IPV4_MASK_LENGTH ) {
+ // checks 0*1* case - Leading zeros in arrayList are truncated
+ return true;
+ } else {
+ //checks 1*0*1 case
+ for(int i=0; i<arrayList.size()-1;i++) {
+ if(arrayList.get(i) ==0 && arrayList.get(i+1) == 1) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static final byte[] convertArbitraryMaskToByteArray(DottedQuad mask) {
+ String maskValue;
+ if (mask.getValue() != null) {
+ maskValue = mask.getValue();
+ } else {
+ maskValue = DEFAULT_ARBITRARY_BIT_MASK;
+ }
+ InetAddress maskInIpFormat = null;
+ try {
+ maskInIpFormat = InetAddress.getByName(maskValue);
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to recognize the host while converting mask ", e);
+ }
+ byte[] bytes = maskInIpFormat.getAddress();
+ return bytes;
+ }
+
+ private static final byte[] convertIpv6ArbitraryMaskToByteArray ( final Ipv6ArbitraryMask mask) {
+ String maskValue;
+ if (mask.getValue() != null) {
+ maskValue = mask.getValue();
+ } else {
+ maskValue = DEFAULT_IPV6_ARBITRARY_BIT_MASK;
+ }
+ InetAddress maskInIpv6Format = null;
+ try {
+ maskInIpv6Format = InetAddress.getByName(maskValue);
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to convert string mask value to ipv6 format ", e);
+ }
+ return maskInIpv6Format.getAddress();
+ }
+
+ static String normalizeIpv4Address(Ipv4Address ipAddress, DottedQuad netMask) {
+ String actualIpAddress="";
+ String[] netMaskParts = netMask.getValue().split("\\.");
+ String[] ipAddressParts = ipAddress.getValue().split("\\.");
+
+ for (int i=0; i<ipAddressParts.length;i++) {
+ int integerFormatIpAddress=Integer.parseInt(ipAddressParts[i]);
+ int integerFormatNetMask=Integer.parseInt(netMaskParts[i]);
+ int ipAddressPart=(integerFormatIpAddress) & (integerFormatNetMask);
+ actualIpAddress += ipAddressPart;
+ if (i != ipAddressParts.length -1 ) {
+ actualIpAddress = actualIpAddress+".";
+ }
+ }
+ return actualIpAddress;
+ }
+
+ private static String normalizeIpv6Address(final Ipv6Address ipAddress, final Ipv6ArbitraryMask netMask) {
+ byte[] ipAddressParts = convertIpv6ToBytes(ipAddress.getValue());
+ byte[] netMaskParts = convertIpv6ToBytes(netMask.getValue());
+ byte[] actualIpv6Bytes = new byte[16];
+
+ for (int i=0; i<ipAddressParts.length;i++) {
+ byte ipAddressPart= (byte) (ipAddressParts[i] & netMaskParts[i]);
+ actualIpv6Bytes[i] = ipAddressPart;
+ }
+ InetAddress ipv6Address = null;
+ try {
+ ipv6Address = InetAddress.getByAddress(actualIpv6Bytes);
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to recognize the host while normalizing IPv6 address from bytes ", e);
+ }
+ return ipv6Address.getHostAddress();
+ }
+
+ private static byte[] convertIpv6ToBytes(final String ipv6Address) {
+ return extractIpv6CanonicalForm(ipv6Address).getAddress();
+ }
+
+ private static InetAddress extractIpv6CanonicalForm(final String ipv6Address) {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(ipv6Address);
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to recognize the host while converting IPv6 to bytes ", e);
+ }
+ return address;
+ }
+
+ static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
+ return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
+ }
+
+ private static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final byte [] bytemask) {
+ return createPrefix(ipv6Address, String.valueOf(countBits(bytemask)));
+ }
+
+ private static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address, final String mask) {
+ if (mask != null && !mask.isEmpty()) {
+ return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + mask);
+ } else {
+ return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + IPV6_ADDRESS_LENGTH);
+ }
+ }
+
+ private static Ipv6Prefix createPrefix(final Ipv6Address ipv6Address) {
+ return new Ipv6Prefix(ipv6Address.getValue() + PREFIX_SEPARATOR + IPV6_ADDRESS_LENGTH);
+ }
+
+ static int countBits(final byte[] mask) {
+ int netmask = 0;
+ for (byte b : mask) {
+ netmask += Integer.bitCount(UnsignedBytes.toInt(b));
+ }
+ return netmask;
+ }
+
+ static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
+ return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
+ }
+
+ static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
+ /*
+ * Ipv4Address has already validated the address part of the prefix,
+ * It is mandated to comply to the same regexp as the address
+ * There is absolutely no point rerunning additional checks vs this
+ * Note - there is no canonical form check here!!!
+ */
+ if (null != mask && !mask.isEmpty()) {
+ return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
+ } else {
+ return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
+ }
+ }
+}