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 java.math.BigInteger;
11 import java.net.Inet4Address;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.Arrays;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpSourceHardwareAddress;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpTargetHardwareAddress;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpTargetHardwareAddressBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchArbitraryBitMask;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 import com.google.common.annotations.VisibleForTesting;
36 import com.google.common.net.InetAddresses;
37 import com.google.common.primitives.UnsignedBytes;
41 * @author sai.marapareddy@gmail.com
44 public class MatchComparatorHelper {
46 private static final Logger LOG = LoggerFactory.getLogger(MatchComparatorHelper.class);
47 private static final int DEFAULT_SUBNET = 32;
48 private static final int IPV4_MASK_LENGTH = 32;
49 private static final int SHIFT_OCTET_1 = 24;
50 private static final int SHIFT_OCTET_2 = 16;
51 private static final int SHIFT_OCTET_3 = 8;
52 private static final int SHIFT_OCTET_4 = 0;
53 private static final int POSITION_OCTET_1 = 0;
54 private static final int POSITION_OCTET_2 = 1;
55 private static final int POSITION_OCTET_3 = 2;
56 private static final int POSITION_OCTET_4 = 3;
57 private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255";
58 private static final String PREFIX_SEPARATOR = "/";
59 private static final int IPV4_ADDRESS_LENGTH = 32;
60 private static final int BYTE_SIZE = 8;
63 * Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
64 * (upper or lower or mix). Ethernet Match which controller receives from switch is always an upper case string.
65 * Default EthernetMatch equals doesn't use equalsIgnoreCase() and hence it fails. E.g User provided mac address
66 * string in flow match is aa:bb:cc:dd:ee:ff and when controller fetch statistic data, openflow driver library
67 * returns AA:BB:CC:DD:EE:FF and default eqauls fails here.
70 static boolean ethernetMatchEquals(final EthernetMatch statsEthernetMatch, final EthernetMatch storedEthernetMatch) {
71 boolean verdict = true;
72 final Boolean checkNullValues = checkNullValues(statsEthernetMatch, storedEthernetMatch);
73 if (checkNullValues != null) {
74 verdict = checkNullValues;
76 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetSource(),
77 storedEthernetMatch.getEthernetSource());
79 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetDestination(),
80 storedEthernetMatch.getEthernetDestination());
83 if (statsEthernetMatch.getEthernetType() == null) {
84 if (storedEthernetMatch.getEthernetType() != null) {
88 verdict = statsEthernetMatch.getEthernetType().equals(storedEthernetMatch.getEthernetType());
95 static boolean ethernetMatchFieldsEquals(final MacAddressFilter statsEthernetMatchFields,
96 final MacAddressFilter storedEthernetMatchFields) {
97 boolean verdict = true;
98 final Boolean checkNullValues = checkNullValues(statsEthernetMatchFields, storedEthernetMatchFields);
99 if (checkNullValues != null) {
100 verdict = checkNullValues;
102 verdict = macAddressEquals(statsEthernetMatchFields.getAddress(), storedEthernetMatchFields.getAddress());
104 verdict = macAddressEquals(statsEthernetMatchFields.getMask(), storedEthernetMatchFields.getMask());
110 static boolean macAddressEquals(final MacAddress statsMacAddress, final MacAddress storedMacAddress) {
111 boolean verdict = true;
112 final Boolean checkNullValues = checkNullValues(statsMacAddress, storedMacAddress);
113 if (checkNullValues != null) {
114 verdict = checkNullValues;
116 verdict = statsMacAddress.getValue().equalsIgnoreCase(storedMacAddress.getValue());
122 static boolean layer3MatchEquals(final Layer3Match statsLayer3Match, final Layer3Match storedLayer3Match) {
123 boolean verdict = true;
124 if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match) {
125 final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
126 final Ipv4Match storedIpv4Match = (Ipv4Match) storedLayer3Match;
127 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(storedIpv4Match.getIpv4Destination(),
128 statsIpv4Match.getIpv4Destination());
130 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(statsIpv4Match.getIpv4Source(),
131 storedIpv4Match.getIpv4Source());
133 } else if (statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6Match) {
134 final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
135 final Ipv6Match storedIpv6Match = (Ipv6Match) storedLayer3Match;
136 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(storedIpv6Match.getIpv6Destination(),
137 statsIpv6Match.getIpv6Destination());
139 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(statsIpv6Match.getIpv6Source(),
140 storedIpv6Match.getIpv6Source());
142 } else if (statsLayer3Match instanceof Ipv4MatchArbitraryBitMask && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
143 // At this moment storedIpv4MatchArbitraryBitMask & statsIpv4MatchArbitraryBitMask will always have non null arbitrary masks.
144 // In case of no / null arbitrary mask, statsLayer3Match will be an instance of Ipv4Match.
145 // Eg:- stats -> 1.0.1.0/255.0.255.0 stored -> 1.1.1.0/255.0.255.0
146 final Ipv4MatchArbitraryBitMask statsIpv4MatchArbitraryBitMask= (Ipv4MatchArbitraryBitMask) statsLayer3Match;
147 final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
148 if ((storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null |
149 storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null)) {
150 if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
151 String storedDstIpAddress = normalizeIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(),
152 storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
153 String statsDstIpAddress = normalizeIpv4Address(statsIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(),
154 statsIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
155 if (MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue(),
156 statsIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue())) {
157 verdict = MatchComparatorHelper.compareStringNullSafe(storedDstIpAddress,
164 if (storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
165 String storedSrcIpAddress = normalizeIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask()
166 ,storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
167 String statsSrcIpAddress = normalizeIpv4Address(statsIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask()
168 ,statsIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
169 if (MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue(),
170 statsIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue())) {
171 verdict = MatchComparatorHelper.compareStringNullSafe(storedSrcIpAddress,
178 final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
179 if (nullCheckOut != null) {
180 verdict = nullCheckOut;
182 verdict = storedLayer3Match.equals(statsLayer3Match);
185 } else if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
186 // Here stored netmask is an instance of Ipv4MatchArbitraryBitMask, when it is pushed in to switch
187 // it automatically converts it in to cidr format in case of certain subnet masks ( consecutive ones or zeroes)
188 // Eg:- stats src/dest -> 1.1.1.0/24 stored src/dest -> 1.1.1.0/255.255.255.0
189 final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
190 final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
191 if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
192 Ipv4Prefix ipv4PrefixDestination;
193 if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask() != null) {
194 byte[] destByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
195 ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(), destByteMask);
197 ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask());
199 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixDestination, statsIpv4Match.getIpv4Destination());
200 if (verdict == false) {
204 if (storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
205 Ipv4Prefix ipv4PrefixSource;
206 if (storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask() != null) {
207 byte[] srcByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
208 ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask(), srcByteMask);
210 ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask());
212 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixSource, statsIpv4Match.getIpv4Source());
214 } else if (statsLayer3Match instanceof ArpMatch && storedLayer3Match instanceof ArpMatch) {
215 verdict = arpMatchEquals((ArpMatch)statsLayer3Match, (ArpMatch)storedLayer3Match);
217 final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
218 if (nullCheckOut != null) {
219 verdict = nullCheckOut;
221 verdict = storedLayer3Match.equals(statsLayer3Match);
227 static boolean arpMatchEquals(final ArpMatch statsArpMatch, final ArpMatch storedArpMatch) {
229 Integer statsOp = statsArpMatch.getArpOp();
230 Integer storedOp = storedArpMatch.getArpOp();
232 Boolean nullCheck = checkNullValues(statsOp, storedOp);
233 if (nullCheck != null) {
234 if (nullCheck == false) {
237 } else if (!statsOp.equals(storedOp)) {
241 Ipv4Prefix statsIp = statsArpMatch.getArpSourceTransportAddress();
242 Ipv4Prefix storedIp = storedArpMatch.getArpSourceTransportAddress();
243 if (!compareIpv4PrefixNullSafe(statsIp, storedIp)) {
247 statsIp = statsArpMatch.getArpTargetTransportAddress();
248 storedIp = storedArpMatch.getArpTargetTransportAddress();
249 if (!compareIpv4PrefixNullSafe(statsIp, storedIp)) {
253 MacAddressFilter statsMac = statsArpMatch.getArpSourceHardwareAddress();
254 MacAddressFilter storedMac = storedArpMatch.getArpSourceHardwareAddress();
255 if (!ethernetMatchFieldsEquals(statsMac, storedMac)) {
259 statsMac = statsArpMatch.getArpTargetHardwareAddress();
260 storedMac = storedArpMatch.getArpTargetHardwareAddress();
261 if (!ethernetMatchFieldsEquals(statsMac, storedMac)) {
270 * TODO: why don't we use the default Ipv4Prefix.equals()?
272 * @param statsIpAddress
273 * @param storedIpAddress
274 * @return true if IPv4prefixes equals
276 static boolean IpAddressEquals(final Ipv4Prefix statsIpAddress, final Ipv4Prefix storedIpAddress) {
277 final IntegerIpAddress statsIpAddressInt = MatchComparatorHelper.strIpToIntIp(statsIpAddress.getValue());
278 final IntegerIpAddress storedIpAddressInt = MatchComparatorHelper.strIpToIntIp(storedIpAddress.getValue());
280 if (ipAndMaskBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
283 if (ipBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
289 static boolean ipAndMaskBasedMatch(final IntegerIpAddress statsIpAddressInt,
290 final IntegerIpAddress storedIpAddressInt) {
291 return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) == (storedIpAddressInt.getIp() & storedIpAddressInt
295 static boolean ipBasedMatch(final IntegerIpAddress statsIpAddressInt, final IntegerIpAddress storedIpAddressInt) {
296 return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
300 private static boolean IpAddressEquals(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
301 final String[] statsIpMask = statsIpv6.getValue().split("/");
302 final String[] storedIpMask = storedIpv6.getValue().split("/");
303 if (! (statsIpMask.length > 1 && storedIpMask.length > 1 && statsIpMask[1].equals(storedIpMask[1]))){
307 final int prefix = Integer.parseInt(statsIpMask[1]);
308 final int byteIndex = prefix/BYTE_SIZE;
309 final int lastByteBits = BYTE_SIZE - (prefix % BYTE_SIZE);
310 final InetAddress statsIp = InetAddresses.forString(statsIpMask[0]);
311 final InetAddress storedIp = InetAddresses.forString(storedIpMask[0]);
312 byte[] statsIpArr = Arrays.copyOfRange(statsIp.getAddress(),0,byteIndex+1);
313 byte[] storedIpArr = Arrays.copyOfRange(storedIp.getAddress(),0,byteIndex+1);
314 statsIpArr[byteIndex] = (byte) (statsIpArr[byteIndex] & (0XFF << lastByteBits));
315 storedIpArr[byteIndex] = (byte) (storedIpArr[byteIndex] & (0XFF << lastByteBits));
316 if(Arrays.equals(statsIpArr,storedIpArr)) {
322 static Boolean checkNullValues(final Object v1, final Object v2) {
323 Boolean verdict = null;
324 if (v1 == null && v2 != null) {
325 verdict = Boolean.FALSE;
326 } else if (v1 != null && v2 == null) {
327 verdict = Boolean.FALSE;
328 } else if (v1 == null && v2 == null) {
329 verdict = Boolean.TRUE;
334 static boolean compareIpv4PrefixNullSafe(final Ipv4Prefix statsIpv4, final Ipv4Prefix storedIpv4) {
335 boolean verdict = true;
336 final Boolean checkDestNullValuesOut = checkNullValues(storedIpv4, statsIpv4);
337 if (checkDestNullValuesOut != null) {
338 verdict = checkDestNullValuesOut;
339 } else if (!IpAddressEquals(statsIpv4, storedIpv4)) {
345 static boolean compareStringNullSafe(final String stringA, final String stringB) {
346 boolean verdict = true;
347 final Boolean checkDestNullValuesOut = checkNullValues(stringA,stringB);
348 if (checkDestNullValuesOut != null) {
349 verdict = checkDestNullValuesOut;
350 } else if (!stringA.equals(stringB)) {
356 private static boolean compareIpv6PrefixNullSafe(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
357 boolean verdict = true;
358 final Boolean checkDestNullValuesOut = checkNullValues(statsIpv6, storedIpv6);
359 if (checkDestNullValuesOut != null) {
360 verdict = checkDestNullValuesOut;
361 } else if (!IpAddressEquals(statsIpv6, storedIpv6)) {
368 * Method return integer version of ip address. Converted int will be mask if mask specified
370 static IntegerIpAddress strIpToIntIp(final String ipAddresss) {
372 final String[] parts = ipAddresss.split("/");
373 final String ip = parts[0];
376 if (parts.length < 2) {
377 prefix = DEFAULT_SUBNET;
379 prefix = Integer.parseInt(parts[1]);
380 if (prefix < 0 || prefix > IPV4_MASK_LENGTH) {
381 final StringBuilder stringBuilder = new StringBuilder(
382 "Valid values for mask are from range 0 - 32. Value ");
383 stringBuilder.append(prefix);
384 stringBuilder.append(" is invalid.");
385 throw new IllegalStateException(stringBuilder.toString());
389 IntegerIpAddress integerIpAddress = null;
391 final Inet4Address addr = ((Inet4Address) InetAddresses.forString(ip));
392 final byte[] addrBytes = addr.getAddress();
393 // FIXME: what is meaning of anding with 0xFF? Probably could be removed.
394 final int ipInt = ((addrBytes[POSITION_OCTET_1] & 0xFF) << SHIFT_OCTET_1)
395 | ((addrBytes[POSITION_OCTET_2] & 0xFF) << SHIFT_OCTET_2)
396 | ((addrBytes[POSITION_OCTET_3] & 0xFF) << SHIFT_OCTET_3)
397 | ((addrBytes[POSITION_OCTET_4] & 0xFF) << SHIFT_OCTET_4);
399 // FIXME: Is this valid?
400 final int mask = 0xffffffff << DEFAULT_SUBNET - prefix;
402 integerIpAddress = new IntegerIpAddress(ipInt, mask);
404 return integerIpAddress;
407 static boolean isArbitraryBitMask(byte[] byteMask) {
408 if (byteMask == null) {
411 ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>();
413 // converting byte array to bits
414 maskInBits = new BigInteger(1, byteMask).toString(2);
415 ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)")));
416 for(String string:stringMaskArrayList){
417 integerMaskArrayList.add(Integer.parseInt(string));
419 return checkArbitraryBitMask(integerMaskArrayList);
423 static boolean checkArbitraryBitMask(ArrayList<Integer> arrayList) {
424 if (arrayList.size()>0 && arrayList.size()< IPV4_MASK_LENGTH ) {
425 // checks 0*1* case - Leading zeros in arrayList are truncated
429 for(int i=0; i<arrayList.size()-1;i++) {
430 if(arrayList.get(i) ==0 && arrayList.get(i+1) == 1) {
438 static final byte[] convertArbitraryMaskToByteArray(DottedQuad mask) {
440 if(mask.getValue() != null && mask != null){
441 maskValue = mask.getValue();
442 } else maskValue = DEFAULT_ARBITRARY_BIT_MASK;
443 InetAddress maskInIpFormat = null;
445 maskInIpFormat = InetAddress.getByName(maskValue);
446 } catch (UnknownHostException e) {
447 LOG.error("Failed to recognize the host while converting mask ", e);
449 byte[] bytes = maskInIpFormat.getAddress();
454 static String normalizeIpv4Address(Ipv4Address ipAddress, DottedQuad netMask) {
455 String actualIpAddress="";
456 String[] netMaskParts = netMask.getValue().split("\\.");
457 String[] ipAddressParts = ipAddress.getValue().split("\\.");
459 for (int i=0; i<ipAddressParts.length;i++) {
460 int integerFormatIpAddress=Integer.parseInt(ipAddressParts[i]);
461 int integerFormatNetMask=Integer.parseInt(netMaskParts[i]);
462 int ipAddressPart=(integerFormatIpAddress) & (integerFormatNetMask);
463 actualIpAddress += ipAddressPart;
464 if (i != ipAddressParts.length -1 ) {
465 actualIpAddress = actualIpAddress+".";
468 return actualIpAddress;
471 static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
472 return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
475 static int countBits(final byte[] mask) {
477 for (byte b : mask) {
478 netmask += Integer.bitCount(UnsignedBytes.toInt(b));
483 static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
484 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
487 static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
489 * Ipv4Address has already validated the address part of the prefix,
490 * It is mandated to comply to the same regexp as the address
491 * There is absolutely no point rerunning additional checks vs this
492 * Note - there is no canonical form check here!!!
494 if (null != mask && !mask.isEmpty()) {
495 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
497 return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);