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