2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.applications.statistics.manager.impl.helper;
10 import com.google.common.net.InetAddresses;
11 import java.net.Inet4Address;
13 import com.google.common.annotations.VisibleForTesting;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
27 public class MatchComparatorHelper {
29 private static final int DEFAULT_SUBNET = 32;
30 private static final int IPV4_MASK_LENGTH = 32;
31 private static final int SHIFT_OCTET_1 = 24;
32 private static final int SHIFT_OCTET_2 = 16;
33 private static final int SHIFT_OCTET_3 = 8;
34 private static final int SHIFT_OCTET_4 = 0;
35 private static final int POSITION_OCTET_1 = 0;
36 private static final int POSITION_OCTET_2 = 1;
37 private static final int POSITION_OCTET_3 = 2;
38 private static final int POSITION_OCTET_4 = 3;
41 * Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
42 * (upper or lower or mix). Ethernet Match which controller receives from switch is always an upper case string.
43 * Default EthernetMatch equals doesn't use equalsIgnoreCase() and hence it fails. E.g User provided mac address
44 * string in flow match is aa:bb:cc:dd:ee:ff and when controller fetch statistic data, openflow driver library
45 * returns AA:BB:CC:DD:EE:FF and default eqauls fails here.
48 static boolean ethernetMatchEquals(final EthernetMatch statsEthernetMatch, final EthernetMatch storedEthernetMatch) {
49 boolean verdict = true;
50 final Boolean checkNullValues = checkNullValues(statsEthernetMatch, storedEthernetMatch);
51 if (checkNullValues != null) {
52 verdict = checkNullValues;
54 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetSource(),
55 storedEthernetMatch.getEthernetSource());
57 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetDestination(),
58 storedEthernetMatch.getEthernetDestination());
61 if (statsEthernetMatch.getEthernetType() == null) {
62 if (storedEthernetMatch.getEthernetType() != null) {
66 verdict = statsEthernetMatch.getEthernetType().equals(storedEthernetMatch.getEthernetType());
73 static boolean ethernetMatchFieldsEquals(final MacAddressFilter statsEthernetMatchFields,
74 final MacAddressFilter storedEthernetMatchFields) {
75 boolean verdict = true;
76 final Boolean checkNullValues = checkNullValues(statsEthernetMatchFields, storedEthernetMatchFields);
77 if (checkNullValues != null) {
78 verdict = checkNullValues;
80 verdict = macAddressEquals(statsEthernetMatchFields.getAddress(), storedEthernetMatchFields.getAddress());
82 verdict = macAddressEquals(statsEthernetMatchFields.getMask(), storedEthernetMatchFields.getMask());
88 static boolean macAddressEquals(final MacAddress statsMacAddress, final MacAddress storedMacAddress) {
89 boolean verdict = true;
90 final Boolean checkNullValues = checkNullValues(statsMacAddress, storedMacAddress);
91 if (checkNullValues != null) {
92 verdict = checkNullValues;
94 verdict = statsMacAddress.getValue().equalsIgnoreCase(storedMacAddress.getValue());
100 static boolean layer3MatchEquals(final Layer3Match statsLayer3Match, final Layer3Match storedLayer3Match) {
101 boolean verdict = true;
102 if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match) {
103 final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
104 final Ipv4Match storedIpv4Match = (Ipv4Match) storedLayer3Match;
105 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(storedIpv4Match.getIpv4Destination(),
106 statsIpv4Match.getIpv4Destination());
108 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(statsIpv4Match.getIpv4Source(),
109 storedIpv4Match.getIpv4Source());
111 } else if(statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6Match) {
112 final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
113 final Ipv6Match storedIpv6Match = (Ipv6Match) storedLayer3Match;
114 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(storedIpv6Match.getIpv6Destination(),
115 statsIpv6Match.getIpv6Destination());
117 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(statsIpv6Match.getIpv6Source(),
118 storedIpv6Match.getIpv6Source());
121 final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
122 if (nullCheckOut != null) {
123 verdict = nullCheckOut;
125 verdict = storedLayer3Match.equals(statsLayer3Match);
134 * TODO: why don't we use the default Ipv4Prefix.equals()?
136 * @param statsIpAddress
137 * @param storedIpAddress
138 * @return true if IPv4prefixes equals
140 static boolean IpAddressEquals(final Ipv4Prefix statsIpAddress, final Ipv4Prefix storedIpAddress) {
141 final IntegerIpAddress statsIpAddressInt = MatchComparatorHelper.strIpToIntIp(statsIpAddress.getValue());
142 final IntegerIpAddress storedIpAddressInt = MatchComparatorHelper.strIpToIntIp(storedIpAddress.getValue());
144 if (ipAndMaskBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
147 if (ipBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
153 static boolean ipAndMaskBasedMatch(final IntegerIpAddress statsIpAddressInt,
154 final IntegerIpAddress storedIpAddressInt) {
155 return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) == (storedIpAddressInt.getIp() & storedIpAddressInt
159 static boolean ipBasedMatch(final IntegerIpAddress statsIpAddressInt, final IntegerIpAddress storedIpAddressInt) {
160 return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
164 private static boolean IpAddressEquals(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
165 final String[] statsIpMask = statsIpv6.getValue().split("/");
166 final String[] storedIpMask = storedIpv6.getValue().split("/");
167 if(! (statsIpMask.length > 1 && storedIpMask.length > 1 && statsIpMask[1].equals(storedIpMask[1]))){
170 if(InetAddresses.forString(statsIpMask[0]).equals(InetAddresses.forString(storedIpMask[0]))){
177 static Boolean checkNullValues(final Object v1, final Object v2) {
178 Boolean verdict = null;
179 if (v1 == null && v2 != null) {
180 verdict = Boolean.FALSE;
181 } else if (v1 != null && v2 == null) {
182 verdict = Boolean.FALSE;
183 } else if (v1 == null && v2 == null) {
184 verdict = Boolean.TRUE;
190 static boolean compareIpv4PrefixNullSafe(final Ipv4Prefix statsIpv4, final Ipv4Prefix storedIpv4) {
191 boolean verdict = true;
192 final Boolean checkDestNullValuesOut = checkNullValues(storedIpv4, statsIpv4);
193 if (checkDestNullValuesOut != null) {
194 verdict = checkDestNullValuesOut;
195 } else if (!IpAddressEquals(statsIpv4, storedIpv4)) {
202 private static boolean compareIpv6PrefixNullSafe(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
203 boolean verdict = true;
204 final Boolean checkDestNullValuesOut = checkNullValues(statsIpv6, storedIpv6);
205 if (checkDestNullValuesOut != null) {
206 verdict = checkDestNullValuesOut;
207 } else if (!IpAddressEquals(statsIpv6, storedIpv6)) {
215 * Method return integer version of ip address. Converted int will be mask if mask specified
217 static IntegerIpAddress strIpToIntIp(final String ipAddresss) {
219 final String[] parts = ipAddresss.split("/");
220 final String ip = parts[0];
223 if (parts.length < 2) {
224 prefix = DEFAULT_SUBNET;
226 prefix = Integer.parseInt(parts[1]);
227 if (prefix < 0 || prefix > IPV4_MASK_LENGTH) {
228 final StringBuilder stringBuilder = new StringBuilder(
229 "Valid values for mask are from range 0 - 32. Value ");
230 stringBuilder.append(prefix);
231 stringBuilder.append(" is invalid.");
232 throw new IllegalStateException(stringBuilder.toString());
236 IntegerIpAddress integerIpAddress = null;
238 final Inet4Address addr = ((Inet4Address) InetAddresses.forString(ip));
239 final byte[] addrBytes = addr.getAddress();
240 // FIXME: what is meaning of anding with 0xFF? Probably could be removed.
241 final int ipInt = ((addrBytes[POSITION_OCTET_1] & 0xFF) << SHIFT_OCTET_1)
242 | ((addrBytes[POSITION_OCTET_2] & 0xFF) << SHIFT_OCTET_2)
243 | ((addrBytes[POSITION_OCTET_3] & 0xFF) << SHIFT_OCTET_3)
244 | ((addrBytes[POSITION_OCTET_4] & 0xFF) << SHIFT_OCTET_4);
246 // FIXME: Is this valid?
247 final int mask = 0xffffffff << DEFAULT_SUBNET - prefix;
249 integerIpAddress = new IntegerIpAddress(ipInt, mask);
251 return integerIpAddress;