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;
15 import java.util.Map.Entry;
16 import java.util.function.BiFunction;
17 import javax.annotation.Nullable;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.InstructionInfo;
20 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
21 import org.opendaylight.genius.mdsalutil.NwConstants;
22 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
23 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
24 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
25 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
27 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv4;
28 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
29 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
30 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
31 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
32 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Destination;
33 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
34 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchCtState;
35 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchInfoHelper;
36 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchTcpDestinationPort;
37 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchTcpSourcePort;
38 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchUdpDestinationPort;
39 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchUdpSourcePort;
40 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;
41 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;
42 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;
43 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;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRange;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.SourcePortRange;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 public final class AclServiceOFFlowBuilder {
53 private static final Logger LOG = LoggerFactory.getLogger(AclServiceOFFlowBuilder.class);
55 private AclServiceOFFlowBuilder() {
59 * Converts IP matches into flows.
62 * @return the map containing the flows and the respective flow id
65 public static Map<String, List<MatchInfoBase>> programIpFlow(Matches matches) {
66 if (matches != null) {
67 AceIp acl = (AceIp) matches.getAceType();
68 Short protocol = acl.getProtocol();
69 if (protocol == null) {
70 return programEtherFlow(acl);
71 } else if (acl.getProtocol() == NwConstants.IP_PROT_TCP) {
72 return programTcpFlow(acl);
73 } else if (acl.getProtocol() == NwConstants.IP_PROT_UDP) {
74 return programUdpFlow(acl);
75 } else if (acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
76 return programIcmpFlow(acl);
77 } else if (acl.getProtocol() != -1) {
78 return programOtherProtocolFlow(acl);
84 /** Converts ether matches to flows.
85 * @param acl the access control list
86 * @return the map containing the flows and the respective flow id
88 public static Map<String,List<MatchInfoBase>> programEtherFlow(AceIp acl) {
89 List<MatchInfoBase> flowMatches = new ArrayList<>();
90 flowMatches.addAll(addSrcIpMatches(acl));
91 flowMatches.addAll(addDstIpMatches(acl));
92 String flowId = "ETHER" + acl.getProtocol();
93 Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
94 flowMatchesMap.put(flowId,flowMatches);
95 return flowMatchesMap;
98 /** Converts generic protocol matches to flows.
100 * @param acl the access control list
101 * @return the map containing the flows and the respective flow id
103 public static Map<String,List<MatchInfoBase>> programOtherProtocolFlow(AceIp acl) {
104 List<MatchInfoBase> flowMatches = new ArrayList<>();
105 flowMatches.addAll(addSrcIpMatches(acl));
106 flowMatches.addAll(addDstIpMatches(acl));
107 if (acl.getAceIpVersion() instanceof AceIpv4) {
108 flowMatches.add(MatchEthernetType.IPV4);
109 } else if (acl.getAceIpVersion() instanceof AceIpv6) {
110 flowMatches.add(MatchEthernetType.IPV6);
112 flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
113 String flowId = "OTHER_PROTO" + acl.getProtocol();
114 Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
115 flowMatchesMap.put(flowId,flowMatches);
116 return flowMatchesMap;
119 /**Converts icmp matches to flows.
120 * @param acl the access control list
121 * @return the map containing the flows and the respective flow id
123 public static Map<String,List<MatchInfoBase>> programIcmpFlow(AceIp acl) {
124 List<MatchInfoBase> flowMatches = new ArrayList<>();
125 flowMatches.addAll(addSrcIpMatches(acl));
126 flowMatches.addAll(addDstIpMatches(acl));
127 //For ICMP port range indicates type and code
128 SourcePortRange sourcePortRange = acl.getSourcePortRange();
129 String flowId = "ICMP_";
130 if (sourcePortRange != null) {
131 if (acl.getAceIpVersion() instanceof AceIpv4) {
132 flowMatches.add(new MatchIcmpv4(sourcePortRange.getLowerPort().getValue().shortValue(),
133 sourcePortRange.getUpperPort().getValue().shortValue()));
134 flowId = flowId + "V4_SOURCE_" + sourcePortRange.getLowerPort().getValue()
135 + sourcePortRange.getUpperPort().getValue();
136 } else if (acl.getAceIpVersion() instanceof AceIpv6) {
137 flowMatches.add(new MatchIcmpv6(sourcePortRange.getLowerPort().getValue().shortValue(),
138 sourcePortRange.getUpperPort().getValue().shortValue()));
139 flowId = flowId + "V6_SOURCE_" + sourcePortRange.getLowerPort().getValue() + "_"
140 + sourcePortRange.getUpperPort().getValue() + "_";
143 DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
144 if (destinationPortRange != null) {
145 if (acl.getAceIpVersion() instanceof AceIpv4) {
146 flowMatches.add(new MatchIcmpv4(destinationPortRange.getLowerPort().getValue().shortValue(),
147 destinationPortRange.getUpperPort().getValue().shortValue()));
148 flowId = flowId + "V4_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
149 + destinationPortRange.getUpperPort().getValue() + "_";
150 } else if (acl.getAceIpVersion() instanceof AceIpv6) {
151 flowMatches.add(new MatchIcmpv6(destinationPortRange.getLowerPort().getValue().shortValue(),
152 destinationPortRange.getUpperPort().getValue().shortValue()));
153 flowId = flowId + "V6_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
154 + destinationPortRange.getUpperPort().getValue() + "_";
158 if (acl.getAceIpVersion() instanceof AceIpv6 && acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
159 // We are aligning our implementation similar to Neutron Firewall driver where a Security
160 // Group rule with "Ethertype as IPv6 and Protocol as icmp" is treated as ICMPV6 SG Rule.
161 flowMatches.add(new MatchIpProtocol(AclConstants.IP_PROT_ICMPV6));
163 flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
165 Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
166 flowMatchesMap.put(flowId,flowMatches);
167 return flowMatchesMap;
170 /**Converts TCP matches to flows.
171 * @param acl the access control list
172 * @return the map containing the flows and the respective flow id
174 public static Map<String,List<MatchInfoBase>> programTcpFlow(AceIp acl) {
175 Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
176 SourcePortRange sourcePortRange = acl.getSourcePortRange();
177 DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
178 if (sourcePortRange == null && destinationPortRange == null) {
179 List<MatchInfoBase> flowMatches = new ArrayList<>();
180 flowMatches.addAll(addSrcIpMatches(acl));
181 flowMatches.addAll(addDstIpMatches(acl));
182 flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
183 String flowId = "TCP_SOURCE_ALL_";
184 flowMatchesMap.put(flowId,flowMatches);
185 return flowMatchesMap;
187 if (sourcePortRange != null) {
188 Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
189 sourcePortRange.getUpperPort().getValue());
190 updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchTcpSourcePort::new, "TCP_SOURCE_");
192 if (destinationPortRange != null) {
193 Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
194 destinationPortRange.getUpperPort().getValue());
195 updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchTcpDestinationPort::new, "TCP_DESTINATION_");
197 return flowMatchesMap;
200 /**Converts UDP matches to flows.
201 * @param acl the access control list
202 * @return the map containing the flows and the respective flow id
204 public static Map<String,List<MatchInfoBase>> programUdpFlow(AceIp acl) {
205 Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
206 SourcePortRange sourcePortRange = acl.getSourcePortRange();
207 DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
208 if (sourcePortRange == null && destinationPortRange == null) {
209 List<MatchInfoBase> flowMatches = new ArrayList<>();
210 flowMatches.addAll(addSrcIpMatches(acl));
211 flowMatches.addAll(addDstIpMatches(acl));
212 flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
213 String flowId = "UDP_SOURCE_ALL_";
214 flowMatchesMap.put(flowId,flowMatches);
215 return flowMatchesMap;
217 if (sourcePortRange != null) {
218 Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
219 sourcePortRange.getUpperPort().getValue());
220 updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchUdpSourcePort::new, "UDP_SOURCE_");
222 if (destinationPortRange != null) {
223 Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
224 destinationPortRange.getUpperPort().getValue());
225 updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchUdpDestinationPort::new, "UDP_DESTINATION_");
228 return flowMatchesMap;
231 private static void updateFlowMatches(AceIp acl, Map<Integer, Integer> portMaskMap,
232 Map<String, List<MatchInfoBase>> flowMatchesMap,
233 BiFunction<Integer, Integer, NxMatchInfoHelper> constructor, String key) {
234 for (Entry<Integer, Integer> entry: portMaskMap.entrySet()) {
235 Integer port = entry.getKey();
236 List<MatchInfoBase> flowMatches = new ArrayList<>();
237 flowMatches.addAll(addSrcIpMatches(acl));
238 flowMatches.addAll(addDstIpMatches(acl));
239 Integer mask = entry.getValue();
240 if (mask != AclConstants.ALL_LAYER4_PORT_MASK) {
241 flowMatches.add(constructor.apply(port, mask));
243 flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
244 String flowId = key + port + "_" + mask;
245 flowMatchesMap.put(flowId, flowMatches);
249 /** Adds source ip matches to the flows.
250 * @param acl the access control list
251 * @return the list of flows.
253 public static List<MatchInfoBase> addSrcIpMatches(AceIp acl) {
254 List<MatchInfoBase> flowMatches = new ArrayList<>();
255 if (acl.getAceIpVersion() instanceof AceIpv4) {
256 flowMatches.add(MatchEthernetType.IPV4);
257 Ipv4Prefix srcNetwork = ((AceIpv4)acl.getAceIpVersion()).getSourceIpv4Network();
258 if (null != srcNetwork && !srcNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
259 flowMatches.add(new MatchIpv4Source(srcNetwork));
261 } else if (acl.getAceIpVersion() instanceof AceIpv6) {
262 flowMatches.add(MatchEthernetType.IPV6);
263 Ipv6Prefix srcNetwork = ((AceIpv6)acl.getAceIpVersion()).getSourceIpv6Network();
264 if (null != srcNetwork && !srcNetwork.getValue().equals(AclConstants.IPV6_ALL_NETWORK)) {
265 flowMatches.add(new MatchIpv6Source(srcNetwork));
271 /** Adds destination ip matches to the flows.
272 * @param acl the access control list
273 * @return the list of flows.
275 public static List<MatchInfoBase> addDstIpMatches(AceIp acl) {
276 List<MatchInfoBase> flowMatches = new ArrayList<>();
277 if (acl.getAceIpVersion() instanceof AceIpv4) {
278 flowMatches.add(MatchEthernetType.IPV4);
279 Ipv4Prefix dstNetwork = ((AceIpv4)acl.getAceIpVersion()).getDestinationIpv4Network();
280 if (null != dstNetwork && !dstNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
281 flowMatches.add(new MatchIpv4Destination(dstNetwork));
283 } else if (acl.getAceIpVersion() instanceof AceIpv6) {
284 flowMatches.add(MatchEthernetType.IPV6);
285 Ipv6Prefix dstNetwork = ((AceIpv6)acl.getAceIpVersion()).getDestinationIpv6Network();
286 if (null != dstNetwork && !dstNetwork.getValue().equals(AclConstants.IPV6_ALL_NETWORK)) {
287 flowMatches.add(new MatchIpv6Destination(dstNetwork));
293 /** Adds LPort matches to the flow.
294 * @param lportTag lport tag
295 * @param conntrackState conntrack state to be used with matches
296 * @param conntrackMask conntrack mask to be used with matches
297 * @param serviceMode ingress or egress service
298 * @return list of matches
300 public static List<MatchInfoBase> addLPortTagMatches(int lportTag, int conntrackState, int conntrackMask,
301 Class<? extends ServiceModeBase> serviceMode) {
302 List<MatchInfoBase> matches = new ArrayList<>();
303 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
304 matches.add(new NxMatchCtState(conntrackState, conntrackMask));
308 /** Returns drop instruction info.
309 * @return drop list of InstructionInfo objects
311 public static List<InstructionInfo> getDropInstructionInfo() {
312 List<InstructionInfo> instructions = new ArrayList<>();
313 List<ActionInfo> actionsInfos = new ArrayList<>();
314 actionsInfos.add(new ActionDrop());
315 instructions.add(new InstructionApplyActions(actionsInfos));
320 * Returns resubmit instruction info to the given table ID.
322 * @param tableId the table id
323 * @return resubmit list of InstructionInfo objects
325 public static List<InstructionInfo> getResubmitInstructionInfo(short tableId) {
326 List<InstructionInfo> instructions = new ArrayList<>();
327 List<ActionInfo> actionsInfos = new ArrayList<>();
328 actionsInfos.add(new ActionNxResubmit(tableId));
329 instructions.add(new InstructionApplyActions(actionsInfos));
334 * Gets the goto instruction info which specifies goto to the specified
337 * @param gotoTableId the goto table id
338 * @return the goto instruction info
340 public static List<InstructionInfo> getGotoInstructionInfo(short gotoTableId) {
341 List<InstructionInfo> instructions = new ArrayList<>();
342 instructions.add(new InstructionGotoTable(gotoTableId));
347 * Converts port range into a set of masked port ranges.
349 * @param portMin the starting port of the range.
350 * @param portMax the ending port of the range.
351 * @return the map containing the port no and their mask.
354 public static Map<Integer,Integer> getLayer4MaskForRange(int portMin, int portMax) {
355 final int[] offset = { 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 };
356 final int[] mask = { 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
357 0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF };
358 int noOfPorts = portMax - portMin + 1;
359 Map<Integer,Integer> portMap = new HashMap<>();
360 if (noOfPorts == 1) {
361 portMap.put(portMin, mask[15]);
363 } else if (noOfPorts == AclConstants.ALL_LAYER4_PORT) {
364 portMap.put(portMin, AclConstants.ALL_LAYER4_PORT_MASK);
367 if (noOfPorts < 0) { // TODO: replace with infrautils.counter in case of high repetitive usage
368 LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
372 String binaryNoOfPorts = Integer.toBinaryString(noOfPorts);
373 if (binaryNoOfPorts.length() > 16) { // TODO: replace with infrautils.counter in case of high repetitive usage
374 LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
378 int medianOffset = 16 - binaryNoOfPorts.length();
379 int medianLength = offset[medianOffset];
381 for (int tempMedian = 0;tempMedian < portMax;) {
382 tempMedian = medianLength + tempMedian;
383 if (portMin < tempMedian) {
389 int currentMedain = median;
390 for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
391 tempMedian = currentMedain - offset[tempMedianOffset];
392 for (;portMin <= tempMedian;) {
393 portMap.put(tempMedian, mask[tempMedianOffset]);
394 currentMedain = tempMedian;
395 tempMedian = tempMedian - offset[tempMedianOffset];
398 currentMedain = median;
399 for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
400 tempMedian = currentMedain + offset[tempMedianOffset];
401 for (;portMax >= tempMedian - 1;) {
402 portMap.put(currentMedain, mask[tempMedianOffset]);
403 currentMedain = tempMedian;
404 tempMedian = tempMedian + offset[tempMedianOffset];