2 * Copyright (c) 2016 Red Hat, 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
9 package org.opendaylight.netvirt.aclservice.utils;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
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;
33 public class AclServiceOFFlowBuilder {
35 private static final Logger LOG =
36 LoggerFactory.getLogger(AclServiceOFFlowBuilder.class);
39 * Converts IP matches into flows.
42 * @return the map containing the flows and the respective flow id
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);
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
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;
77 /** Converts generic protocol matches to flows.
79 * @param acl the access control list
80 * @return the map containing the flows and the respective flow id
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 }));
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;
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
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() + "_";
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() + "_";
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;
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
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;
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) }));
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);
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) }));
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);
202 return flowMatchesMap;
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
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;
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) }));
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);
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) }));
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);
258 return flowMatchesMap;
261 /** Adds source ip matches to the flows.
262 * @param acl the access control list
263 * @return the list of flows.
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]}));
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()}));
288 /** Adds destination ip matches to the flows.
289 * @param acl the access control list
290 * @return the list of flows.
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]}));
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()}));
316 * Converts port range into a set of masked port ranges.
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.
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]);
332 } else if (noOfPorts == AclConstants.ALL_LAYER4_PORT) {
333 portMap.put(portMin, AclConstants.ALL_LAYER4_PORT_MASK);
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,
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,
347 int medianOffset = 16 - binaryNoOfPorts.length();
348 int medianLength = offset[medianOffset];
350 for (int tempMedian = 0;tempMedian < portMax;) {
351 tempMedian = medianLength + tempMedian;
352 if (portMin < tempMedian) {
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];
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];