aab3a19251801c93698de30c3945352d2a920ef2
[netvirt.git] /
1 /*
2  * Copyright (c) 2016 Red Hat, 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
9 package org.opendaylight.netvirt.aclservice.utils;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import org.opendaylight.genius.mdsalutil.MatchFieldType;
17 import org.opendaylight.genius.mdsalutil.MatchInfo;
18 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
19 import org.opendaylight.genius.mdsalutil.NwConstants;
20 import org.opendaylight.genius.mdsalutil.NxMatchFieldType;
21 import org.opendaylight.genius.mdsalutil.NxMatchInfo;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRange;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.SourcePortRange;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 public class AclServiceOFFlowBuilder {
34
35     private static final Logger LOG =
36             LoggerFactory.getLogger(AclServiceOFFlowBuilder.class);
37
38     /**
39      * Converts IP matches into flows.
40      * @param matches
41      *            the matches
42      * @return the map containing the flows and the respective flow id
43      */
44     public static Map<String, List<MatchInfoBase>> programIpFlow(Matches matches) {
45         if (matches != null) {
46             AceIp acl = (AceIp) matches.getAceType();
47             Short protocol = acl.getProtocol();
48             if (protocol == null) {
49                 return programEtherFlow(acl);
50             } else if (acl.getProtocol() == NwConstants.IP_PROT_TCP) {
51                 return programTcpFlow(acl);
52             } else if (acl.getProtocol() == NwConstants.IP_PROT_UDP) {
53                 return programUdpFlow(acl);
54             } else if (acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
55                 return programIcmpFlow(acl);
56             } else if (acl.getProtocol() != -1) {
57                 return programOtherProtocolFlow(acl);
58             }
59         }
60         return null;
61     }
62
63     /** Converts ether  matches to flows.
64      * @param acl the access control list
65      * @return the map containing the flows and the respective flow id
66      */
67     public static Map<String,List<MatchInfoBase>> programEtherFlow(AceIp acl) {
68         List<MatchInfoBase> flowMatches = new ArrayList<>();
69         flowMatches.addAll(addSrcIpMatches(acl));
70         flowMatches.addAll(addDstIpMatches(acl));
71         String flowId = "ETHER" + acl.getProtocol();
72         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
73         flowMatchesMap.put(flowId,flowMatches);
74         return flowMatchesMap;
75     }
76
77     /** Converts generic protocol matches to flows.
78      *
79      * @param acl the access control list
80      * @return the map containing the flows and the respective flow id
81      */
82     public static Map<String,List<MatchInfoBase>> programOtherProtocolFlow(AceIp acl) {
83         List<MatchInfoBase> flowMatches = new ArrayList<>();
84         flowMatches.addAll(addSrcIpMatches(acl));
85         flowMatches.addAll(addDstIpMatches(acl));
86         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
87             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
88                 new long[] { NwConstants.ETHTYPE_IPV4 }));
89         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
90             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
91                 new long[] { NwConstants.ETHTYPE_IPV6 }));
92         }
93         flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
94             new long[] { acl.getProtocol() }));
95         String flowId = "OTHER_PROTO" + acl.getProtocol();
96         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
97         flowMatchesMap.put(flowId,flowMatches);
98         return flowMatchesMap;
99     }
100
101     /**Converts icmp matches to flows.
102      * @param acl the access control list
103      * @return the map containing the flows and the respective flow id
104      */
105     public static Map<String,List<MatchInfoBase>> programIcmpFlow(AceIp acl) {
106         List<MatchInfoBase> flowMatches = new ArrayList<>();
107         flowMatches.addAll(addSrcIpMatches(acl));
108         flowMatches.addAll(addDstIpMatches(acl));
109         //For ICMP port range indicates type and code
110         SourcePortRange sourcePortRange = acl.getSourcePortRange();
111         String flowId = "ICMP_";
112         if (sourcePortRange != null) {
113             if (acl.getAceIpVersion() instanceof AceIpv4 ) {
114                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v4,
115                     new long[] { sourcePortRange.getLowerPort().getValue(),
116                                  sourcePortRange.getUpperPort().getValue() }));
117                 flowId = flowId + "V4_SOURCE_" + sourcePortRange.getLowerPort().getValue()
118                         + sourcePortRange.getUpperPort().getValue();
119             } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
120                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v6,
121                     new long[] { sourcePortRange.getLowerPort().getValue(),
122                                  sourcePortRange.getUpperPort().getValue() }));
123                 flowId = flowId + "V6_SOURCE_" + sourcePortRange.getLowerPort().getValue() + "_"
124                         + sourcePortRange.getUpperPort().getValue() + "_";
125             }
126         }
127         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
128         if (destinationPortRange != null) {
129             if (acl.getAceIpVersion() instanceof AceIpv4 ) {
130                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v4,
131                     new long[] { destinationPortRange.getLowerPort().getValue(),
132                                  destinationPortRange.getUpperPort().getValue() }));
133                 flowId = flowId + "V4_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
134                         + destinationPortRange.getUpperPort().getValue() + "_";
135             } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
136                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v6,
137                     new long[] { destinationPortRange.getLowerPort().getValue(),
138                                  destinationPortRange.getUpperPort().getValue() }));
139                 flowId = flowId + "V6_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
140                         + destinationPortRange.getUpperPort().getValue() + "_";
141             }
142         }
143         flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
144             new long[] { acl.getProtocol() }));
145         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
146         flowMatchesMap.put(flowId,flowMatches);
147         return flowMatchesMap;
148     }
149
150     /**Converts TCP matches to flows.
151      * @param acl the access control list
152      * @return the map containing the flows and the respective flow id
153      */
154     public static Map<String,List<MatchInfoBase>> programTcpFlow(AceIp acl) {
155         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
156         SourcePortRange sourcePortRange = acl.getSourcePortRange();
157         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
158         if (sourcePortRange == null && destinationPortRange == null) {
159             List<MatchInfoBase> flowMatches = new ArrayList<>();
160             flowMatches.addAll(addSrcIpMatches(acl));
161             flowMatches.addAll(addDstIpMatches(acl));
162             flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
163                 new long[] { acl.getProtocol() }));
164             String flowId = "TCP_SOURCE_ALL_";
165             flowMatchesMap.put(flowId,flowMatches);
166             return flowMatchesMap;
167         }
168         if (sourcePortRange != null) {
169             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
170                 sourcePortRange.getUpperPort().getValue());
171             for (Integer port: portMaskMap.keySet()) {
172                 List<MatchInfoBase> flowMatches = new ArrayList<>();
173                 flowMatches.addAll(addSrcIpMatches(acl));
174                 flowMatches.addAll(addDstIpMatches(acl));
175                 if (portMaskMap.get(port) != AclConstants.ALL_LAYER4_PORT_MASK) {
176                     flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_tcp_src_with_mask,
177                         new long[] {  port, portMaskMap.get(port) }));
178                 }
179                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
180                     new long[] { acl.getProtocol() }));
181                 String flowId = "TCP_SOURCE_" + port + "_" + portMaskMap.get(port);
182                 flowMatchesMap.put(flowId,flowMatches);
183             }
184         }
185         if (destinationPortRange != null) {
186             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
187                 destinationPortRange.getUpperPort().getValue());
188             for (Integer port: portMaskMap.keySet()) {
189                 List<MatchInfoBase> flowMatches = new ArrayList<>();
190                 flowMatches.addAll(addSrcIpMatches(acl));
191                 flowMatches.addAll(addDstIpMatches(acl));
192                 if (portMaskMap.get(port) != AclConstants.ALL_LAYER4_PORT_MASK) {
193                     flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_tcp_dst_with_mask,
194                         new long[] {  port, portMaskMap.get(port) }));
195                 }
196                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
197                     new long[] { acl.getProtocol() }));
198                 String flowId = "TCP_DESTINATION_" + port + "_" + portMaskMap.get(port);
199                 flowMatchesMap.put(flowId,flowMatches);
200             }
201         }
202         return flowMatchesMap;
203     }
204
205     /**Converts UDP matches to flows.
206      * @param acl the access control list
207      * @return the map containing the flows and the respective flow id
208      */
209     public static Map<String,List<MatchInfoBase>> programUdpFlow(AceIp acl) {
210         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
211         SourcePortRange sourcePortRange = acl.getSourcePortRange();
212         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
213         if (sourcePortRange == null && destinationPortRange == null) {
214             List<MatchInfoBase> flowMatches = new ArrayList<>();
215             flowMatches.addAll(addSrcIpMatches(acl));
216             flowMatches.addAll(addDstIpMatches(acl));
217             flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
218                 new long[] { acl.getProtocol() }));
219             String flowId = "UDP_SOURCE_ALL_";
220             flowMatchesMap.put(flowId,flowMatches);
221             return flowMatchesMap;
222         }
223         if (sourcePortRange != null) {
224             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
225                 sourcePortRange.getUpperPort().getValue());
226             for (Integer port: portMaskMap.keySet()) {
227                 List<MatchInfoBase> flowMatches = new ArrayList<>();
228                 flowMatches.addAll(addSrcIpMatches(acl));
229                 flowMatches.addAll(addDstIpMatches(acl));
230                 if (portMaskMap.get(port) != AclConstants.ALL_LAYER4_PORT_MASK) {
231                     flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_udp_src_with_mask,
232                         new long[] {  port, portMaskMap.get(port) }));
233                 }
234                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
235                     new long[] { acl.getProtocol() }));
236                 String flowId = "UDP_SOURCE_" + port + "_" + portMaskMap.get(port);
237                 flowMatchesMap.put(flowId ,flowMatches);
238             }
239         }
240         if (destinationPortRange != null) {
241             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
242                 destinationPortRange.getUpperPort().getValue());
243             for (Integer port: portMaskMap.keySet()) {
244                 List<MatchInfoBase> flowMatches = new ArrayList<>();
245                 flowMatches.addAll(addSrcIpMatches(acl));
246                 flowMatches.addAll(addDstIpMatches(acl));
247                 if (portMaskMap.get(port) != AclConstants.ALL_LAYER4_PORT_MASK) {
248                     flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_udp_dst_with_mask,
249                         new long[] {  port, portMaskMap.get(port) }));
250                 }
251                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
252                     new long[] { acl.getProtocol() }));
253                 String flowId = "UDP_DESTINATION_" + port + "_" + portMaskMap.get(port);
254                 flowMatchesMap.put(flowId, flowMatches);
255             }
256         }
257
258         return flowMatchesMap;
259     }
260
261     /** Adds source ip matches to the flows.
262      * @param acl the access control list
263      * @return the list of flows.
264      */
265     public static List<MatchInfoBase> addSrcIpMatches(AceIp acl) {
266         List<MatchInfoBase> flowMatches = new ArrayList<>();
267         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
268             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
269                 new long[] { NwConstants.ETHTYPE_IPV4 }));
270             Ipv4Prefix srcNetwork = ((AceIpv4)acl.getAceIpVersion()).getSourceIpv4Network();
271             if (null != srcNetwork && !srcNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
272                 String[] ipaddressValues = srcNetwork.getValue().split("/");
273                 flowMatches.add(new MatchInfo(MatchFieldType.ipv4_source,
274                     new String[] {ipaddressValues[0], ipaddressValues[1]}));
275             }
276         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
277             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
278                 new long[] { NwConstants.ETHTYPE_IPV6 }));
279             Ipv6Prefix srcNetwork = ((AceIpv6)acl.getAceIpVersion()).getSourceIpv6Network();
280             if (null != srcNetwork) {
281                 flowMatches.add(new MatchInfo(MatchFieldType.ipv6_source,
282                     new String[] {srcNetwork.getValue()}));
283             }
284         }
285         return flowMatches;
286     }
287
288     /** Adds destination ip matches to the flows.
289      * @param acl the access control list
290      * @return the list of flows.
291      */
292     public static List<MatchInfoBase> addDstIpMatches(AceIp acl) {
293         List<MatchInfoBase> flowMatches = new ArrayList<>();
294         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
295             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
296                 new long[] { NwConstants.ETHTYPE_IPV4 }));
297             Ipv4Prefix dstNetwork = ((AceIpv4)acl.getAceIpVersion()).getDestinationIpv4Network();
298             if (null != dstNetwork && !dstNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
299                 String[] ipaddressValues = dstNetwork.getValue().split("/");
300                 flowMatches.add(new MatchInfo(MatchFieldType.ipv4_destination,
301                     new String[] {ipaddressValues[0], ipaddressValues[1]}));
302             }
303         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
304             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
305                 new long[] { NwConstants.ETHTYPE_IPV6 }));
306             Ipv6Prefix dstNetwork = ((AceIpv6)acl.getAceIpVersion()).getDestinationIpv6Network();
307             if (null != dstNetwork) {
308                 flowMatches.add(new MatchInfo(MatchFieldType.ipv6_destination,
309                     new String[] {dstNetwork.getValue()}));
310             }
311         }
312         return flowMatches;
313     }
314
315     /**
316      * Converts port range into a set of masked port ranges.
317      *
318      * @param portMin the starting port of the range.
319      * @param portMax the ending port of the range.
320      * @return the map containing the port no and their mask.
321      *
322      */
323     public static Map<Integer,Integer>  getLayer4MaskForRange(int portMin, int portMax) {
324         final int[] offset = { 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 };
325         final int[] mask = { 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
326             0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF };
327         int noOfPorts = portMax - portMin + 1;
328         Map<Integer,Integer> portMap = new HashMap<>();
329         if (noOfPorts == 1) {
330             portMap.put(portMin, mask[15]);
331             return portMap;
332         } else if (noOfPorts == AclConstants.ALL_LAYER4_PORT) {
333             portMap.put(portMin, AclConstants.ALL_LAYER4_PORT_MASK);
334             return portMap;
335         }
336         if (noOfPorts < 0) { // TODO: replace with infrautils.counter in case of high repetitive usage
337             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
338                     portMax);
339             return portMap;
340         }
341         String binaryNoOfPorts = Integer.toBinaryString(noOfPorts);
342         if (binaryNoOfPorts.length() > 16) { // TODO: replace with infrautils.counter in case of high repetitive usage
343             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
344                     portMax);
345             return portMap;
346         }
347         int medianOffset = 16 - binaryNoOfPorts.length();
348         int medianLength = offset[medianOffset];
349         int median = 0;
350         for (int tempMedian = 0;tempMedian < portMax;) {
351             tempMedian = medianLength + tempMedian;
352             if (portMin < tempMedian) {
353                 median = tempMedian;
354                 break;
355             }
356         }
357         int tempMedian = 0;
358         int currentMedain = median;
359         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
360             tempMedian = currentMedain - offset[tempMedianOffset];
361             if (portMin <= tempMedian) {
362                 for (;portMin <= tempMedian;) {
363                     portMap.put(tempMedian, mask[tempMedianOffset]);
364                     currentMedain = tempMedian;
365                     tempMedian = tempMedian - offset[tempMedianOffset];
366                 }
367             }
368         }
369         currentMedain = median;
370         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
371             tempMedian = currentMedain + offset[tempMedianOffset];
372             if (portMax >= tempMedian - 1) {
373                 for (;portMax >= tempMedian - 1;) {
374                     portMap.put(currentMedain, mask[tempMedianOffset]);
375                     currentMedain = tempMedian;
376                     tempMedian = tempMedian  + offset[tempMedianOffset];
377                 }
378             }
379         }
380         return portMap;
381     }
382
383 }