2 * Copyright (c) 2018 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
8 package org.opendaylight.netvirt.aclservice;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.List;
16 import java.util.stream.Collectors;
17 import org.opendaylight.genius.mdsalutil.FlowEntity;
18 import org.opendaylight.genius.mdsalutil.InstructionInfo;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
24 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
25 import org.opendaylight.genius.mdsalutil.matches.MatchArpSha;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetSource;
27 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
28 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
29 import org.opendaylight.genius.utils.ServiceIndex;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.mdsal.binding.api.DataBroker;
32 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
33 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
34 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
35 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
36 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
37 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
38 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
39 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl.InterfaceType;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.SubnetInfo;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.Uint64;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * Provides the implementation for egress (w.r.t VM) ACL service.
59 * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
62 public class EgressAclServiceImpl extends AbstractAclServiceImpl {
64 private static final Logger LOG = LoggerFactory.getLogger(EgressAclServiceImpl.class);
67 * Initialize the member variables.
69 public EgressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
70 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
71 // Service mode is w.rt. switch
72 super(ServiceModeIngress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils,
73 jobCoordinator, aclInterfaceCache);
79 * @param aclInterface the acl interface
82 public void bindService(AclInterface aclInterface) {
83 String interfaceName = aclInterface.getInterfaceId();
84 LOG.debug("Binding ACL service for interface {}", interfaceName);
85 if (aclInterface.getInterfaceType() == InterfaceType.DhcpService) {
86 LOG.debug("{} is a DHCP serice port. No binding needed", interfaceName);
89 jobCoordinator.enqueueJob(interfaceName, () -> {
90 int instructionKey = 0;
91 List<Instruction> instructions = new ArrayList<>();
92 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(getAclAntiSpoofingTable(), ++instructionKey));
93 short serviceIndex = ServiceIndex.getIndex(AclConstants.INGRESS_ACL_SERVICE_NAME,
94 AclConstants.INGRESS_ACL_SERVICE_INDEX);
95 int flowPriority = AclConstants.INGRESS_ACL_SERVICE_INDEX;
96 BoundServices serviceInfo =
97 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "ingressacl", interfaceName),
98 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
99 InstanceIdentifier<BoundServices> path =
100 AclServiceUtils.buildServiceId(interfaceName, serviceIndex, serviceMode);
102 return Collections.singletonList(
103 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.mergeParentStructurePut(
104 path, serviceInfo)));
111 * @param aclInterface the acl interface
114 protected void unbindService(AclInterface aclInterface) {
115 String interfaceName = aclInterface.getInterfaceId();
116 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
117 ServiceIndex.getIndex(NwConstants.ACL_SERVICE_NAME, NwConstants.ACL_SERVICE_INDEX), serviceMode);
119 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
120 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
121 .callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.delete(path))));
125 * Programs DHCP Service flows.
127 * @param flowEntries the flow entries
128 * @param port the acl interface
129 * @param action add/modify/remove action
130 * @param addOrRemove addorRemove
133 protected void programDhcpService(List<FlowEntity> flowEntries, AclInterface port,
134 Action action, int addOrRemove) {
135 // No action required on egress.
139 * Programs DHCP service flows.
141 * @param flowEntries the flow entries
142 * @param port the acl interface
143 * @param allowedAddresses the allowed addresses
144 * @param addOrRemove addorRemove
147 protected void processDhcpServiceUpdate(List<FlowEntity> flowEntries, AclInterface port,
148 List<AllowedAddressPairs> allowedAddresses, int addOrRemove) {
149 // No action required on egress.
153 protected void programAntiSpoofingRules(List<FlowEntity> flowEntries, AclInterface port,
154 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
155 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
156 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
158 Uint64 dpid = Uint64.valueOf(port.getDpId());
159 int lportTag = port.getLPortTag();
160 if (action != Action.UPDATE) {
161 programCommitterDropFlow(flowEntries, dpid, lportTag, addOrRemove);
162 egressAclIcmpv6AllowedList(flowEntries, dpid, lportTag, addOrRemove);
164 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddresses);
165 if (!hasDuplicateMac(port.getAllowedAddressPairs(), filteredAAPs, action)) {
166 programL2BroadcastAllowRule(flowEntries, port, filteredAAPs, addOrRemove);
167 egressAclDhcpAllowClientTraffic(flowEntries, port, filteredAAPs, lportTag, addOrRemove);
168 egressAclDhcpv6AllowClientTraffic(flowEntries, port, filteredAAPs, lportTag, addOrRemove);
170 programArpRule(flowEntries, dpid, filteredAAPs, lportTag, addOrRemove);
173 private void programCommitterDropFlow(List<FlowEntity> flowEntries, Uint64 dpId, int lportTag,
175 List<MatchInfoBase> matches = new ArrayList<>();
176 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
178 Uint64 metaData = Uint64.fromLongBits(MetaDataUtil.getLportTagMetaData(lportTag).longValue()
179 | MetaDataUtil.getAclDropMetaData(AclConstants.METADATA_DROP_FLAG).longValue());
180 Uint64 metaDataMask = Uint64.fromLongBits(MetaDataUtil.METADATA_MASK_LPORT_TAG.longValue()
181 | MetaDataUtil.METADATA_MASK_ACL_DROP.longValue());
182 matches.add(new MatchMetadata(metaData, metaDataMask));
184 String flowName = "Egress_" + dpId + "_" + lportTag + "_Drop";
185 addFlowEntryToList(flowEntries, dpId, getAclCommitterTable(), flowName,
186 AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, 0, 0, AclServiceUtils.getDropFlowCookie(lportTag),
187 matches, instructions, addOrRemove);
191 protected void programRemoteAclTableFlow(List<FlowEntity> flowEntries, Uint64 dpId, Integer aclTag,
192 AllowedAddressPairs aap, int addOrRemove) {
193 List<MatchInfoBase> flowMatches = new ArrayList<>();
194 flowMatches.addAll(AclServiceUtils.buildIpAndDstServiceMatch(aclTag, aap));
196 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
197 String flowNameAdded = "Acl_Filter_Egress_" + aap.getIpAddress().stringValue() + "_" + aclTag;
199 addFlowEntryToList(flowEntries, dpId, getAclRemoteAclTable(), flowNameAdded,
200 AclConstants.ACL_DEFAULT_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, flowMatches,
201 instructions, addOrRemove);
205 protected void programGotoClassifierTableRules(List<FlowEntity> flowEntries, Uint64 dpId,
206 List<AllowedAddressPairs> aaps, int lportTag, int addOrRemove) {
207 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(aaps);
208 for (AllowedAddressPairs aap : filteredAAPs) {
209 IpPrefixOrAddress attachIp = aap.getIpAddress();
210 MacAddress mac = aap.getMacAddress();
212 List<MatchInfoBase> matches = new ArrayList<>();
213 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
214 matches.add(new MatchEthernetSource(mac));
215 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_SOURCE));
217 List<InstructionInfo> gotoInstructions = new ArrayList<>();
218 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
220 String flowName = "Egress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_"
221 + mac.getValue() + "_" + attachIp.stringValue();
222 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
223 AclConstants.PROTO_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions,
229 * Add rule to allow certain ICMPv6 traffic from VM ports.
231 * @param flowEntries the flow entries
232 * @param dpId the dpId
233 * @param lportTag the lport tag
234 * @param addOrRemove add/remove the flow.
236 private void egressAclIcmpv6AllowedList(List<FlowEntity> flowEntries, Uint64 dpId, int lportTag,
238 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
240 for (Integer icmpv6Type: AclConstants.allowedIcmpv6NdList()) {
241 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(icmpv6Type, 0, lportTag, serviceMode);
242 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + icmpv6Type + "_Permit_";
243 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
244 AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
245 instructions, addOrRemove);
250 * Add rule to ensure only DHCP server traffic from the specified mac is allowed.
252 * @param flowEntries the flow entries
253 * @param port the Acl Interface port
254 * @param allowedAddresses the allowed addresses
255 * @param lportTag the lport tag
256 * @param addOrRemove whether to add or remove the flow
258 private void egressAclDhcpAllowClientTraffic(List<FlowEntity> flowEntries, AclInterface port,
259 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
260 Uint64 dpId = Uint64.valueOf(port.getDpId());
261 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
262 for (AllowedAddressPairs aap : allowedAddresses) {
263 if (!AclServiceUtils.isIPv4Address(aap)) {
266 List<MatchInfoBase> matches = new ArrayList<>();
267 matches.addAll(AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_CLIENT_PORT_IPV4,
268 AclConstants.DHCP_SERVER_PORT_IPV4, lportTag, serviceMode));
269 matches.add(new MatchEthernetSource(aap.getMacAddress()));
272 "Egress_DHCP_Client_v4" + dpId + "_" + lportTag + "_" + aap.getMacAddress().getValue() + "_Permit_";
273 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
274 AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
275 matches, instructions, addOrRemove);
280 * Add rule to ensure only DHCPv6 server traffic from the specified mac is allowed.
282 * @param flowEntries the flow entries
283 * @param port the Acl Interface port
284 * @param allowedAddresses the allowed addresses
285 * @param lportTag the lport tag
286 * @param addOrRemove whether to add or remove the flow
288 private void egressAclDhcpv6AllowClientTraffic(List<FlowEntity> flowEntries, AclInterface port,
289 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
290 Uint64 dpId = Uint64.valueOf(port.getDpId());
291 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
292 for (AllowedAddressPairs aap : allowedAddresses) {
293 if (AclServiceUtils.isIPv4Address(aap)) {
296 List<MatchInfoBase> matches = new ArrayList<>();
297 matches.addAll(AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_CLIENT_PORT_IPV6,
298 AclConstants.DHCP_SERVER_PORT_IPV6, lportTag, serviceMode));
299 matches.add(new MatchEthernetSource(aap.getMacAddress()));
301 String flowName = "Egress_DHCP_Client_v6" + "_" + dpId + "_" + lportTag + "_"
302 + aap.getMacAddress().getValue() + "_Permit_";
303 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
304 AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
305 matches, instructions, addOrRemove);
310 * Adds the rule to allow arp packets.
312 * @param flowEntries the flow entries
313 * @param dpId the dpId
314 * @param allowedAddresses the allowed addresses
315 * @param lportTag the lport tag
316 * @param addOrRemove whether to add or remove the flow
318 protected void programArpRule(List<FlowEntity> flowEntries, Uint64 dpId,
319 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
320 for (AllowedAddressPairs allowedAddress : allowedAddresses) {
321 if (!AclServiceUtils.isIPv4Address(allowedAddress)) {
322 continue; // For IPv6 allowed addresses
325 IpPrefixOrAddress allowedAddressIp = allowedAddress.getIpAddress();
326 MacAddress allowedAddressMac = allowedAddress.getMacAddress();
327 List<MatchInfoBase> arpIpMatches = AclServiceUtils.buildArpIpMatches(allowedAddressIp);
328 List<MatchInfoBase> matches = new ArrayList<>();
329 matches.add(MatchEthernetType.ARP);
330 matches.add(new MatchArpSha(allowedAddressMac));
331 matches.add(new MatchEthernetSource(allowedAddressMac));
332 matches.addAll(arpIpMatches);
333 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
335 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
336 LOG.debug("{} ARP Rule on DPID {}, lportTag {}",
337 addOrRemove == NwConstants.DEL_FLOW ? "Deleting" : "Adding", dpId, lportTag);
338 String flowName = "Egress_ARP_" + dpId + "_" + lportTag + "_" + allowedAddress.getMacAddress().getValue()
339 + allowedAddressIp.stringValue();
340 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
341 AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
342 instructions, addOrRemove);
347 protected void programIcmpv6RARule(List<FlowEntity> flowEntries, AclInterface port, List<SubnetInfo> subnets,
349 // No action required on egress.
353 * Programs broadcast rules.
355 * @param flowEntries the flow entries
356 * @param port the Acl Interface port
357 * @param addOrRemove whether to delete or add flow
360 protected void programBroadcastRules(List<FlowEntity> flowEntries, AclInterface port, Action action,
362 List<AllowedAddressPairs> allowedAddressPairs = port.getAllowedAddressPairs();
363 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddressPairs);
364 if (!hasDuplicateMac(allowedAddressPairs, filteredAAPs, action)) {
365 programL2BroadcastAllowRule(flowEntries, port, filteredAAPs, addOrRemove);
370 * Programs broadcast rules.
372 * @param flowEntries the flow entries
373 * @param port the Acl Interface port
374 * @param subnetInfoList the port subnet info list
375 * @param addOrRemove whether to delete or add flow
377 protected void programSubnetBroadcastRules(List<FlowEntity> flowEntries, AclInterface port,
378 List<SubnetInfo> subnetInfoList, int addOrRemove) {
379 // No action required on egress.
383 * Programs Non-IP broadcast rules.
385 * @param flowEntries the flow entries
386 * @param port the Acl Interface port
387 * @param filteredAAPs the filtered AAPs list
388 * @param addOrRemove whether to delete or add flow
390 private void programL2BroadcastAllowRule(List<FlowEntity> flowEntries, AclInterface port,
391 List<AllowedAddressPairs> filteredAAPs, int addOrRemove) {
392 Uint64 dpId = Uint64.valueOf(port.getDpId());
393 int lportTag = port.getLPortTag();
394 Set<MacAddress> macs = filteredAAPs.stream().map(AllowedAddressPairs::getMacAddress)
395 .collect(Collectors.toSet());
396 for (MacAddress mac : macs) {
397 List<MatchInfoBase> matches = new ArrayList<>();
398 matches.add(new MatchEthernetSource(mac));
399 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
401 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
403 String flowName = "Egress_L2Broadcast_" + dpId + "_" + lportTag + "_" + mac.getValue();
404 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
405 AclConstants.PROTO_L2BROADCAST_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
406 matches, instructions, addOrRemove);
410 private boolean hasDuplicateMac(List<AllowedAddressPairs> allowedAddresses,
411 List<AllowedAddressPairs> filteredAAPs, Action action) {
412 // Do not proceed further if Port Up OR down event.
413 if (action != Action.UPDATE) {
416 // exclude multicastAAPs from PortBefore/PortAfter AAPs
417 List<AllowedAddressPairs> excludeMulticastAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddresses);
418 // GET Delta of ADDED/DELETED aaps with PortBefore/PortAfter-AAPs
419 List<AllowedAddressPairs> filteredAllowedAddresses = excludeMulticastAAPs.stream().filter(
420 aap -> !filteredAAPs.contains(aap)).collect(Collectors.toList());
421 Set<MacAddress> macs = filteredAAPs.stream().map(aap -> aap.getMacAddress()).collect(Collectors.toSet());
422 List<AllowedAddressPairs> aapWithDuplicateMac = filteredAllowedAddresses.stream()
423 .filter(entry -> macs.contains(entry.getMacAddress())).collect(Collectors.toList());
425 return !aapWithDuplicateMac.isEmpty();
429 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
430 return direction.equals(DirectionEgress.class);
433 private short getAclAntiSpoofingTable() {
434 return NwConstants.INGRESS_ACL_ANTI_SPOOFING_TABLE;
437 private short getAclConntrackClassifierTable() {
438 return NwConstants.INGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
442 protected short getAclConntrackSenderTable() {
443 return NwConstants.INGRESS_ACL_CONNTRACK_SENDER_TABLE;
447 protected short getAclForExistingTrafficTable() {
448 return NwConstants.INGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
452 protected short getAclFilterCumDispatcherTable() {
453 return NwConstants.INGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
457 protected short getAclRuleBasedFilterTable() {
458 return NwConstants.INGRESS_ACL_RULE_BASED_FILTER_TABLE;
462 protected short getAclRemoteAclTable() {
463 return NwConstants.INGRESS_REMOTE_ACL_TABLE;
467 protected short getAclCommitterTable() {
468 return NwConstants.INGRESS_ACL_COMMITTER_TABLE;