1682da69a657aaef184a3577d727054cfddc348a
[openflowplugin.git] / applications / statistics-manager / src / main / java / org / opendaylight / openflowplugin / applications / statistics / manager / impl / helper / MatchComparatorHelper.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.applications.statistics.manager.impl.helper;
9
10 import com.google.common.net.InetAddresses;
11 import java.math.BigInteger;
12 import java.net.Inet4Address;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19 import com.google.common.annotations.VisibleForTesting;
20 import com.google.common.primitives.UnsignedBytes;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
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.Ipv6Match;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchArbitraryBitMask;
32
33 /**
34  * @author joe
35  * @author sai.marapareddy@gmail.com
36  *
37  */
38 public class MatchComparatorHelper {
39
40     private static final Logger LOG = LoggerFactory.getLogger(MatchComparatorHelper.class);
41     private static final int DEFAULT_SUBNET = 32;
42     private static final int IPV4_MASK_LENGTH = 32;
43     private static final int SHIFT_OCTET_1 = 24;
44     private static final int SHIFT_OCTET_2 = 16;
45     private static final int SHIFT_OCTET_3 = 8;
46     private static final int SHIFT_OCTET_4 = 0;
47     private static final int POSITION_OCTET_1 = 0;
48     private static final int POSITION_OCTET_2 = 1;
49     private static final int POSITION_OCTET_3 = 2;
50     private static final int POSITION_OCTET_4 = 3;
51     private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255";
52     private static final String PREFIX_SEPARATOR = "/";
53     private static final int IPV4_ADDRESS_LENGTH = 32;
54
55     /*
56      * Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
57      * (upper or lower or mix). Ethernet Match which controller receives from switch is always an upper case string.
58      * Default EthernetMatch equals doesn't use equalsIgnoreCase() and hence it fails. E.g User provided mac address
59      * string in flow match is aa:bb:cc:dd:ee:ff and when controller fetch statistic data, openflow driver library
60      * returns AA:BB:CC:DD:EE:FF and default eqauls fails here.
61      */
62     @VisibleForTesting
63     static boolean ethernetMatchEquals(final EthernetMatch statsEthernetMatch, final EthernetMatch storedEthernetMatch) {
64         boolean verdict = true;
65         final Boolean checkNullValues = checkNullValues(statsEthernetMatch, storedEthernetMatch);
66         if (checkNullValues != null) {
67             verdict = checkNullValues;
68         } else {
69             verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetSource(),
70                     storedEthernetMatch.getEthernetSource());
71             if (verdict) {
72                 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetDestination(),
73                         storedEthernetMatch.getEthernetDestination());
74             }
75             if (verdict) {
76                 if (statsEthernetMatch.getEthernetType() == null) {
77                     if (storedEthernetMatch.getEthernetType() != null) {
78                         verdict = false;
79                     }
80                 } else {
81                     verdict = statsEthernetMatch.getEthernetType().equals(storedEthernetMatch.getEthernetType());
82                 }
83             }
84         }
85         return verdict;
86     }
87
88     static boolean ethernetMatchFieldsEquals(final MacAddressFilter statsEthernetMatchFields,
89             final MacAddressFilter storedEthernetMatchFields) {
90         boolean verdict = true;
91         final Boolean checkNullValues = checkNullValues(statsEthernetMatchFields, storedEthernetMatchFields);
92         if (checkNullValues != null) {
93             verdict = checkNullValues;
94         } else {
95             verdict = macAddressEquals(statsEthernetMatchFields.getAddress(), storedEthernetMatchFields.getAddress());
96             if (verdict) {
97                 verdict = macAddressEquals(statsEthernetMatchFields.getMask(), storedEthernetMatchFields.getMask());
98             }
99         }
100         return verdict;
101     }
102
103     static boolean macAddressEquals(final MacAddress statsMacAddress, final MacAddress storedMacAddress) {
104         boolean verdict = true;
105         final Boolean checkNullValues = checkNullValues(statsMacAddress, storedMacAddress);
106         if (checkNullValues != null) {
107             verdict = checkNullValues;
108         } else {
109             verdict = statsMacAddress.getValue().equalsIgnoreCase(storedMacAddress.getValue());
110         }
111         return verdict;
112     }
113
114     @VisibleForTesting
115     static boolean layer3MatchEquals(final Layer3Match statsLayer3Match, final Layer3Match storedLayer3Match) {
116         boolean verdict = true;
117         if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match) {
118             final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
119             final Ipv4Match storedIpv4Match = (Ipv4Match) storedLayer3Match;
120             verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(storedIpv4Match.getIpv4Destination(),
121                     statsIpv4Match.getIpv4Destination());
122             if (verdict) {
123                 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(statsIpv4Match.getIpv4Source(),
124                         storedIpv4Match.getIpv4Source());
125             }
126         } else if(statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6Match) {
127             final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
128             final Ipv6Match storedIpv6Match = (Ipv6Match) storedLayer3Match;
129             verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(storedIpv6Match.getIpv6Destination(),
130                     statsIpv6Match.getIpv6Destination());
131             if (verdict) {
132                 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(statsIpv6Match.getIpv6Source(),
133                         storedIpv6Match.getIpv6Source());
134             }
135         } else if(statsLayer3Match instanceof  Ipv4MatchArbitraryBitMask && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
136             // At this moment storedIpv4MatchArbitraryBitMask & statsIpv4MatchArbitraryBitMask will always have non null arbitrary masks.
137             // In case of no / null arbitrary mask, statsLayer3Match will be an instance of Ipv4Match.
138             // Eg:- stats -> 1.0.1.0/255.0.255.0  stored -> 1.1.1.0/255.0.255.0
139             final Ipv4MatchArbitraryBitMask statsIpv4MatchArbitraryBitMask= (Ipv4MatchArbitraryBitMask) statsLayer3Match;
140             final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
141             if((storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null |
142                     storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null)) {
143                 if(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
144                         String storedIpAddress = extractIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(),
145                                 storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
146                         if(MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue(),
147                                 statsIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask().getValue())) {
148                             verdict = MatchComparatorHelper.compareStringNullSafe(storedIpAddress,
149                                     statsIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask().getValue());
150                         }
151                         else {
152                             verdict = false;
153                             return verdict;
154                         }
155                 }
156                 if(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
157                         String storedIpAddress = extractIpv4Address(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask()
158                                 ,storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
159                         if(MatchComparatorHelper.compareStringNullSafe(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue(),
160                                 statsIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask().getValue())) {
161                             verdict = MatchComparatorHelper.compareStringNullSafe(storedIpAddress,
162                                     statsIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask().getValue());
163                         }
164                         else {
165                             verdict = false;
166                         }
167                 }
168             }
169             else {
170                 final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
171                 if (nullCheckOut != null) {
172                     verdict = nullCheckOut;
173                 } else {
174                     verdict = storedLayer3Match.equals(statsLayer3Match);
175                 }
176             }
177         }
178         else if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4MatchArbitraryBitMask) {
179             // Here stored netmask is an instance of Ipv4MatchArbitraryBitMask, when it is pushed in to switch
180             // it automatically converts it in to cidr format in case of certain subnet masks ( consecutive ones or zeroes)
181             // Eg:- stats src/dest -> 1.1.1.0/24  stored src/dest -> 1.1.1.0/255.255.255.0
182             final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
183             final Ipv4MatchArbitraryBitMask storedIpv4MatchArbitraryBitMask = (Ipv4MatchArbitraryBitMask) storedLayer3Match;
184             if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask() != null) {
185                 Ipv4Prefix ipv4PrefixDestination;
186                 if (storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask() != null) {
187                     byte[] destByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4DestinationArbitraryBitmask());
188                     ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask(), destByteMask);
189                 }
190                 else{
191                     ipv4PrefixDestination = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4DestinationAddressNoMask());
192                 }
193                 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixDestination, statsIpv4Match.getIpv4Destination());
194                 if(verdict == false) {
195                     return verdict;
196                 }
197             }
198             if (storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask() != null) {
199                 Ipv4Prefix ipv4PrefixSource;
200                 if (storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask() != null) {
201                     byte[] srcByteMask = convertArbitraryMaskToByteArray(storedIpv4MatchArbitraryBitMask.getIpv4SourceArbitraryBitmask());
202                     ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask(), srcByteMask);
203                 }
204                 else {
205                     ipv4PrefixSource = createPrefix(storedIpv4MatchArbitraryBitMask.getIpv4SourceAddressNoMask());
206                 }
207                 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(ipv4PrefixSource, statsIpv4Match.getIpv4Source());
208             }
209         } else {
210             final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
211             if (nullCheckOut != null) {
212                 verdict = nullCheckOut;
213             } else {
214                 verdict = storedLayer3Match.equals(statsLayer3Match);
215             }
216         }
217         return verdict;
218     }
219
220
221     /**
222      * TODO: why don't we use the default Ipv4Prefix.equals()?
223      *
224      * @param statsIpAddress
225      * @param storedIpAddress
226      * @return true if IPv4prefixes equals
227      */
228     static boolean IpAddressEquals(final Ipv4Prefix statsIpAddress, final Ipv4Prefix storedIpAddress) {
229         final IntegerIpAddress statsIpAddressInt = MatchComparatorHelper.strIpToIntIp(statsIpAddress.getValue());
230         final IntegerIpAddress storedIpAddressInt = MatchComparatorHelper.strIpToIntIp(storedIpAddress.getValue());
231
232         if (ipAndMaskBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
233             return true;
234         }
235         if (ipBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
236             return true;
237         }
238         return false;
239     }
240
241     static boolean ipAndMaskBasedMatch(final IntegerIpAddress statsIpAddressInt,
242             final IntegerIpAddress storedIpAddressInt) {
243         return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) == (storedIpAddressInt.getIp() & storedIpAddressInt
244                 .getMask()));
245     }
246
247     static boolean ipBasedMatch(final IntegerIpAddress statsIpAddressInt, final IntegerIpAddress storedIpAddressInt) {
248         return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
249     }
250
251
252     private static boolean IpAddressEquals(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
253         final String[] statsIpMask = statsIpv6.getValue().split("/");
254         final String[] storedIpMask = storedIpv6.getValue().split("/");
255         if(! (statsIpMask.length > 1 && storedIpMask.length > 1 &&  statsIpMask[1].equals(storedIpMask[1]))){
256             return false;
257         }
258         if(InetAddresses.forString(statsIpMask[0]).equals(InetAddresses.forString(storedIpMask[0]))){
259             return true;
260         }
261         return false;
262     }
263
264
265     static Boolean checkNullValues(final Object v1, final Object v2) {
266         Boolean verdict = null;
267         if (v1 == null && v2 != null) {
268             verdict = Boolean.FALSE;
269         } else if (v1 != null && v2 == null) {
270             verdict = Boolean.FALSE;
271         } else if (v1 == null && v2 == null) {
272             verdict = Boolean.TRUE;
273         }
274
275         return verdict;
276     }
277
278     static boolean compareIpv4PrefixNullSafe(final Ipv4Prefix statsIpv4, final Ipv4Prefix storedIpv4) {
279         boolean verdict = true;
280         final Boolean checkDestNullValuesOut = checkNullValues(storedIpv4, statsIpv4);
281         if (checkDestNullValuesOut != null) {
282             verdict = checkDestNullValuesOut;
283         } else if (!IpAddressEquals(statsIpv4, storedIpv4)) {
284             verdict = false;
285         }
286         return verdict;
287     }
288
289     static boolean compareStringNullSafe(final String stringA, final String stringB) {
290         boolean verdict = true;
291         final Boolean checkDestNullValuesOut = checkNullValues(stringA,stringB);
292         if (checkDestNullValuesOut != null) {
293             verdict = checkDestNullValuesOut;
294         }else if (!stringA.equals(stringB)) {
295             verdict = false;
296         }
297         return verdict;
298     }
299
300     private static boolean compareIpv6PrefixNullSafe(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
301         boolean verdict = true;
302         final Boolean checkDestNullValuesOut = checkNullValues(statsIpv6, storedIpv6);
303         if (checkDestNullValuesOut != null) {
304             verdict = checkDestNullValuesOut;
305         } else if (!IpAddressEquals(statsIpv6, storedIpv6)) {
306             verdict = false;
307         }
308
309         return verdict;
310     }
311
312     /**
313      * Method return integer version of ip address. Converted int will be mask if mask specified
314      */
315     static IntegerIpAddress strIpToIntIp(final String ipAddresss) {
316
317         final String[] parts = ipAddresss.split("/");
318         final String ip = parts[0];
319         int prefix;
320
321         if (parts.length < 2) {
322             prefix = DEFAULT_SUBNET;
323         } else {
324             prefix = Integer.parseInt(parts[1]);
325             if (prefix < 0 || prefix > IPV4_MASK_LENGTH) {
326                 final StringBuilder stringBuilder = new StringBuilder(
327                         "Valid values for mask are from range 0 - 32. Value ");
328                 stringBuilder.append(prefix);
329                 stringBuilder.append(" is invalid.");
330                 throw new IllegalStateException(stringBuilder.toString());
331             }
332         }
333
334         IntegerIpAddress integerIpAddress = null;
335
336         final Inet4Address addr = ((Inet4Address) InetAddresses.forString(ip));
337         final byte[] addrBytes = addr.getAddress();
338         // FIXME: what is meaning of anding with 0xFF? Probably could be removed.
339         final int ipInt = ((addrBytes[POSITION_OCTET_1] & 0xFF) << SHIFT_OCTET_1)
340                 | ((addrBytes[POSITION_OCTET_2] & 0xFF) << SHIFT_OCTET_2)
341                 | ((addrBytes[POSITION_OCTET_3] & 0xFF) << SHIFT_OCTET_3)
342                 | ((addrBytes[POSITION_OCTET_4] & 0xFF) << SHIFT_OCTET_4);
343
344         // FIXME: Is this valid?
345         final int mask = 0xffffffff << DEFAULT_SUBNET - prefix;
346
347         integerIpAddress = new IntegerIpAddress(ipInt, mask);
348
349         return integerIpAddress;
350     }
351
352     static boolean isArbitraryBitMask(byte[] byteMask) {
353         if (byteMask == null) {
354             return false;
355         }
356         else {
357             ArrayList<Integer> integerMaskArrayList = new ArrayList<Integer>();
358             String maskInBits;
359             // converting byte array to bits
360             maskInBits = new BigInteger(1, byteMask).toString(2);
361             ArrayList<String> stringMaskArrayList = new ArrayList<String>(Arrays.asList(maskInBits.split("(?!^)")));
362             for(String string:stringMaskArrayList){
363                 integerMaskArrayList.add(Integer.parseInt(string));
364             }
365             return checkArbitraryBitMask(integerMaskArrayList);
366         }
367     }
368
369     static boolean checkArbitraryBitMask(ArrayList<Integer> arrayList) {
370         // checks 0*1* case - Leading zeros in arrayList are truncated
371         if(arrayList.size()>0 && arrayList.size()<32) {
372             return true;
373         }
374         //checks 1*0*1 case
375         else {
376             for(int i=0; i<arrayList.size()-1;i++) {
377                 if(arrayList.get(i) ==0 && arrayList.get(i+1) == 1) {
378                     return true;
379                 }
380             }
381         }
382         return false;
383     }
384
385     static final byte[] convertArbitraryMaskToByteArray(DottedQuad mask) {
386         String maskValue;
387         if(mask.getValue() != null && mask != null){
388             maskValue  = mask.getValue();
389         }
390         else maskValue = DEFAULT_ARBITRARY_BIT_MASK;
391         InetAddress maskInIpFormat = null;
392         try {
393             maskInIpFormat = InetAddress.getByName(maskValue);
394         } catch (UnknownHostException e) {
395             LOG.error("Failed to recognize the host while converting mask ", e);
396         }
397         byte[] bytes = maskInIpFormat.getAddress();
398         return bytes;
399     }
400
401
402     static String extractIpv4Address(Ipv4Address ipAddress, DottedQuad netMask) {
403         String actualIpAddress="";
404         String[] netMaskParts = netMask.getValue().split("\\.");
405         String[] ipAddressParts = ipAddress.getValue().split("\\.");
406
407         for(int i=0; i<ipAddressParts.length;i++) {
408             int integerFormatIpAddress=Integer.parseInt(ipAddressParts[i]);
409             int integerFormatNetMask=Integer.parseInt(netMaskParts[i]);
410             int ipAddressPart=(integerFormatIpAddress) & (integerFormatNetMask);
411             actualIpAddress += ipAddressPart;
412             if(i != ipAddressParts.length -1 ) {
413                 actualIpAddress = actualIpAddress+".";
414             }
415         }
416         return actualIpAddress;
417     }
418
419     static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final byte [] bytemask){
420         return createPrefix(ipv4Address, String.valueOf(countBits(bytemask)));
421     }
422
423     static int countBits(final byte[] mask) {
424         int netmask = 0;
425         for (byte b : mask) {
426             netmask += Integer.bitCount(UnsignedBytes.toInt(b));
427         }
428         return netmask;
429     }
430
431     static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address){
432         return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
433     }
434
435     static Ipv4Prefix createPrefix(final Ipv4Address ipv4Address, final String mask){
436         /*
437          * Ipv4Address has already validated the address part of the prefix,
438          * It is mandated to comply to the same regexp as the address
439          * There is absolutely no point rerunning additional checks vs this
440          * Note - there is no canonical form check here!!!
441          */
442         if (null != mask && !mask.isEmpty()) {
443             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + mask);
444         } else {
445             return new Ipv4Prefix(ipv4Address.getValue() + PREFIX_SEPARATOR + IPV4_ADDRESS_LENGTH);
446         }
447     }
448 }