2 * Copyright (c) 2016 HPE, 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
8 package org.opendaylight.netvirt.aclservice;
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.InstructionInfo;
20 import org.opendaylight.genius.mdsalutil.MDSALUtil;
21 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
22 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
25 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
26 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
27 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
28 import org.opendaylight.genius.utils.ServiceIndex;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
31 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
32 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
33 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
34 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
35 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
39 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;
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.AceType;
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.yang.types.rev130715.Uuid;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * Provides abstract implementation for ingress (w.r.t VM) ACL service.
59 * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
62 public abstract class AbstractIngressAclServiceImpl extends AbstractAclServiceImpl {
64 private static final Logger LOG = LoggerFactory.getLogger(AbstractIngressAclServiceImpl.class);
67 * Initialize the member variables.
69 * @param dataBroker the data broker instance.
70 * @param mdsalManager the mdsal manager.
73 * @param aclServiceUtils
74 * the acl service util.
76 public AbstractIngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
77 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator) {
78 // Service mode is w.rt. switch
79 super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator);
85 * @param aclInterface the acl interface
88 public void bindService(AclInterface aclInterface) {
89 String interfaceName = aclInterface.getInterfaceId();
90 jobCoordinator.enqueueJob(interfaceName,
92 int instructionKey = 0;
93 List<Instruction> instructions = new ArrayList<>();
94 Long vpnId = aclInterface.getVpnId();
96 instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getVpnIdMetadata(vpnId),
97 MetaDataUtil.METADATA_MASK_VRFID, ++instructionKey));
98 LOG.debug("Binding ACL service for interface {} with vpnId {}", interfaceName, vpnId);
100 Long elanTag = aclInterface.getElanId();
102 MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getElanTagMetadata(elanTag),
103 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
104 LOG.debug("Binding ACL service for interface {} with ElanTag {}", interfaceName, elanTag);
107 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_TABLE, ++instructionKey));
108 int flowPriority = AclConstants.INGRESS_ACL_DEFAULT_FLOW_PRIORITY;
109 short serviceIndex = ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME,
110 NwConstants.EGRESS_ACL_SERVICE_INDEX);
111 BoundServices serviceInfo = AclServiceUtils.getBoundServices(
112 String.format("%s.%s.%s", "acl", "ingressacl", interfaceName), serviceIndex, flowPriority,
113 AclConstants.COOKIE_ACL_BASE, instructions);
114 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
115 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME,
116 NwConstants.EGRESS_ACL_SERVICE_INDEX), ServiceModeEgress.class);
118 WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
119 writeTxn.put(LogicalDatastoreType.CONFIGURATION, path, serviceInfo,
120 WriteTransaction.CREATE_MISSING_PARENTS);
122 return Collections.singletonList(writeTxn.submit());
129 * @param aclInterface the acl interface
132 protected void unbindService(AclInterface aclInterface) {
133 String interfaceName = aclInterface.getInterfaceId();
134 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
135 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
136 ServiceModeEgress.class);
138 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
139 jobCoordinator.enqueueJob(interfaceName,
141 WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
142 writeTxn.delete(LogicalDatastoreType.CONFIGURATION, path);
144 return Collections.singletonList(writeTxn.submit());
149 * Program conntrack rules.
151 * @param dpid the dpid
152 * @param dhcpMacAddress the dhcp mac address.
153 * @param allowedAddresses the allowed addresses
154 * @param lportTag the lport tag
155 * @param addOrRemove add or remove the flow
158 protected abstract void programSpecificFixedRules(BigInteger dpid, String dhcpMacAddress,
159 List<AllowedAddressPairs> allowedAddresses, int lportTag, String portId, Action action, int addOrRemove);
162 protected void programGeneralFixedRules(AclInterface port, String dhcpMacAddress,
163 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
164 LOG.info("programFixedRules : {} default rules.", action == Action.ADD ? "adding" : "removing");
166 BigInteger dpid = port.getDpId();
167 int lportTag = port.getLPortTag();
168 if (action == Action.ADD || action == Action.REMOVE) {
169 ingressAclDhcpAllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
170 AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
171 ingressAclDhcpv6AllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
172 AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
173 ingressAclIcmpv6AllowedTraffic(dpid, lportTag, addOrRemove);
175 programArpRule(dpid, lportTag, addOrRemove);
176 programIpv4BroadcastRule(port, addOrRemove);
181 protected void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag, List<AllowedAddressPairs> deletedAAP,
182 List<AllowedAddressPairs> addedAAP) {
183 // Nothing to do for port update as ingress ARP flow is based only on lportTag
188 protected boolean programAclRules(AclInterface port, List<Uuid> aclUuidList,int addOrRemove) {
189 BigInteger dpId = port.getDpId();
190 LOG.debug("Applying custom rules on DpId {}, lportTag {}", dpId, port.getLPortTag());
191 if (aclUuidList == null || dpId == null) {
192 LOG.warn("one of the ingress acl parameters can not be null. sg {}, dpId {}",
197 for (Uuid sgUuid :aclUuidList) {
198 Acl acl = AclServiceUtils.getAcl(dataBroker, sgUuid.getValue());
200 LOG.warn("The ACL is empty");
203 AccessListEntries accessListEntries = acl.getAccessListEntries();
204 List<Ace> aceList = accessListEntries.getAce();
205 for (Ace ace : aceList) {
206 programAceRule(port, addOrRemove, acl.getAclName(), ace, null);
213 protected void programAceRule(AclInterface port, int addOrRemove, String aclName, Ace ace,
214 List<AllowedAddressPairs> syncAllowedAddresses) {
215 SecurityRuleAttr aceAttr = AclServiceUtils.getAccesssListAttributes(ace);
216 if (!aceAttr.getDirection().equals(DirectionIngress.class)) {
219 Matches matches = ace.getMatches();
220 AceType aceType = matches.getAceType();
221 Map<String, List<MatchInfoBase>> flowMap = null;
222 if (aceType instanceof AceIp) {
223 flowMap = AclServiceOFFlowBuilder.programIpFlow(matches);
224 if (syncAllowedAddresses != null) {
225 flowMap = AclServiceUtils.getFlowForAllowedAddresses(syncAllowedAddresses, flowMap, true);
226 } else if (aceAttr.getRemoteGroupId() != null) {
227 flowMap = aclServiceUtils.getFlowForRemoteAcl(port, aceAttr.getRemoteGroupId(), port.getInterfaceId(),
231 int lportTag = port.getLPortTag();
232 if (null == flowMap) {
233 LOG.error("Failed to apply ACL {} lportTag {}", ace.getKey(), lportTag);
236 for (String flowName : flowMap.keySet()) {
237 syncSpecificAclFlow(port.getDpId(), lportTag, addOrRemove, ace, port.getInterfaceId(), flowMap, flowName);
242 protected void updateRemoteAclTableForPort(AclInterface port, Uuid acl, int addOrRemove,
243 AllowedAddressPairs ip, BigInteger aclId, BigInteger dpId) {
244 Long elanTag = port.getElanId();
245 Long vpnId = port.getVpnId();
246 List<MatchInfoBase> flowMatches = new ArrayList<>();
247 flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(elanTag, ip, dataBroker, vpnId));
249 List<InstructionInfo> instructions = new ArrayList<>();
251 InstructionWriteMetadata writeMetatdata =
252 new InstructionWriteMetadata(AclServiceUtils.getAclIdMetadata(aclId),
253 MetaDataUtil.METADATA_MASK_REMOTE_ACL_ID);
254 instructions.add(writeMetatdata);
255 instructions.add(new InstructionGotoTable(getIngressAclFilterTable()));
257 Long serviceTag = vpnId != null ? vpnId : elanTag;
258 String flowNameAdded = "Acl_Filter_Ingress_" + new String(ip.getIpAddress().getValue()) + "_" + serviceTag;
260 syncFlow(dpId, getIngressAclRemoteAclTable(), flowNameAdded, AclConstants.NO_PRIORITY, "ACL", 0, 0,
261 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
264 protected short getIngressAclFilterTable() {
265 return NwConstants.EGRESS_ACL_FILTER_TABLE;
268 protected short getIngressAclRemoteAclTable() {
269 return NwConstants.EGRESS_ACL_REMOTE_ACL_TABLE;
272 protected abstract String syncSpecificAclFlow(BigInteger dpId, int lportTag, int addOrRemove, Ace ace,
273 String portId, Map<String, List<MatchInfoBase>> flowMap, String flowName);
276 * Add rule to ensure only DHCP server traffic from the specified mac is
279 * @param dpId the dpid
280 * @param dhcpMacAddress the DHCP server mac address
281 * @param lportTag the lport tag
282 * @param addOrRemove is write or delete
283 * @param protoPortMatchPriority the priority
285 protected void ingressAclDhcpAllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
286 int addOrRemove, int protoPortMatchPriority) {
287 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
288 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, ServiceModeIngress.class);
290 List<ActionInfo> actionsInfos = new ArrayList<>();
291 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
293 String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_" + dhcpMacAddress + "_Permit_";
294 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0,
295 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
299 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
302 * @param dpId the dpid
303 * @param dhcpMacAddress the DHCP server mac address
304 * @param lportTag the lport tag
305 * @param addOrRemove is write or delete
306 * @param protoPortMatchPriority the priority
308 protected void ingressAclDhcpv6AllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
309 int addOrRemove, Integer protoPortMatchPriority) {
310 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
311 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, ServiceModeIngress.class);
313 List<ActionInfo> actionsInfos = new ArrayList<>();
314 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
317 "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_" + "_" + dhcpMacAddress + "_Permit_";
318 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0,
319 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
323 * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), NS (135), NA (136) are allowed into the VM.
325 * @param dpId the dpid
326 * @param lportTag the lport tag
327 * @param addOrRemove is write or delete
329 private void ingressAclIcmpv6AllowedTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
330 List<ActionInfo> actionsInfos = new ArrayList<>();
331 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(actionsInfos);
333 // Allow ICMPv6 Multicast Listener Query packets.
334 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY,
335 0, lportTag, ServiceModeIngress.class);
338 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
339 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0,
340 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
342 // Allow ICMPv6 Neighbor Solicitation packets.
343 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag,
344 ServiceModeIngress.class);
347 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
348 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0,
349 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
351 // Allow ICMPv6 Neighbor Advertisement packets.
352 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag,
353 ServiceModeIngress.class);
356 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
357 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0,
358 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
362 * Adds the rule to allow arp packets.
364 * @param dpId the dpId
365 * @param lportTag the lport tag
366 * @param addOrRemove whether to add or remove the flow
368 protected void programArpRule(BigInteger dpId, int lportTag, int addOrRemove) {
369 List<MatchInfoBase> matches = new ArrayList<>();
370 matches.add(MatchEthernetType.ARP);
371 matches.add(buildLPortTagMatch(lportTag));
372 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(new ArrayList<>());
373 LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
374 + "lportTag {}", dpId, lportTag);
375 String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
376 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName,
377 AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0, 0,
378 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
383 * Programs broadcast rules.
385 * @param port the Acl Interface port
386 * @param addOrRemove whether to delete or add flow
389 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
390 programIpv4BroadcastRule(port, addOrRemove);
394 * Programs IPv4 broadcast rules.
396 * @param port the Acl Interface port
397 * @param addOrRemove whether to delete or add flow
399 private void programIpv4BroadcastRule(AclInterface port, int addOrRemove) {
400 BigInteger dpId = port.getDpId();
401 int lportTag = port.getLPortTag();
402 MatchInfoBase lportMatchInfo = buildLPortTagMatch(lportTag);
403 List<IpPrefixOrAddress> cidrs = port.getSubnetIpPrefixes();
405 List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(cidrs);
406 for (String broadcastAddress : broadcastAddresses) {
407 List<MatchInfoBase> matches =
408 AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
409 matches.add(lportMatchInfo);
410 List<InstructionInfo> instructions = new ArrayList<>();
411 instructions.add(new InstructionGotoTable(NwConstants.EGRESS_ACL_REMOTE_ACL_TABLE));
412 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
413 syncFlow(dpId, NwConstants.EGRESS_ACL_TABLE, flowName,
414 AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
415 instructions, addOrRemove);
418 LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
422 protected MatchInfoBase buildLPortTagMatch(int lportTag) {
423 return AclServiceUtils.buildLPortTagMatch(lportTag, ServiceModeIngress.class);