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.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
18 import java.util.stream.Collectors;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.genius.mdsalutil.FlowEntity;
21 import org.opendaylight.genius.mdsalutil.InstructionInfo;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
24 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
25 import org.opendaylight.genius.mdsalutil.NwConstants;
26 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.genius.mdsalutil.matches.MatchArpSha;
29 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetSource;
30 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
31 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
32 import org.opendaylight.genius.utils.ServiceIndex;
33 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
34 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
35 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
36 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
37 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
38 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
39 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
40 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
41 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
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.ServiceModeIngress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
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.interfaces._interface.AllowedAddressPairs;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfo;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
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 jobCoordinator.enqueueJob(interfaceName, () -> {
85 int instructionKey = 0;
86 List<Instruction> instructions = new ArrayList<>();
87 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(getAclAntiSpoofingTable(), ++instructionKey));
88 short serviceIndex = ServiceIndex.getIndex(AclConstants.INGRESS_ACL_SERVICE_NAME,
89 AclConstants.INGRESS_ACL_SERVICE_INDEX);
90 int flowPriority = AclConstants.INGRESS_ACL_SERVICE_INDEX;
91 BoundServices serviceInfo =
92 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "ingressacl", interfaceName),
93 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
94 InstanceIdentifier<BoundServices> path =
95 AclServiceUtils.buildServiceId(interfaceName, serviceIndex, serviceMode);
97 return Collections.singletonList(
98 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.put(
99 path, serviceInfo, CREATE_MISSING_PARENTS)));
106 * @param aclInterface the acl interface
109 protected void unbindService(AclInterface aclInterface) {
110 String interfaceName = aclInterface.getInterfaceId();
111 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
112 ServiceIndex.getIndex(NwConstants.ACL_SERVICE_NAME, NwConstants.ACL_SERVICE_INDEX), serviceMode);
114 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
115 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
116 .callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.delete(path))));
120 protected void programAntiSpoofingRules(List<FlowEntity> flowEntries, AclInterface port,
121 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
122 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
123 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
125 BigInteger dpid = port.getDpId();
126 int lportTag = port.getLPortTag();
127 if (action != Action.UPDATE) {
128 programCommitterDropFlow(flowEntries, dpid, lportTag, addOrRemove);
129 egressAclIcmpv6AllowedList(flowEntries, dpid, lportTag, addOrRemove);
131 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddresses);
132 programL2BroadcastAllowRule(flowEntries, port, filteredAAPs, addOrRemove);
134 egressAclDhcpAllowClientTraffic(flowEntries, port, filteredAAPs, lportTag, addOrRemove);
135 egressAclDhcpv6AllowClientTraffic(flowEntries, port, filteredAAPs, lportTag, addOrRemove);
136 programArpRule(flowEntries, dpid, filteredAAPs, lportTag, addOrRemove);
139 private void programCommitterDropFlow(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag,
141 List<MatchInfoBase> matches = new ArrayList<>();
142 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
144 BigInteger metaData = MetaDataUtil.getLportTagMetaData(lportTag)
145 .or(MetaDataUtil.getAclDropMetaData(AclConstants.METADATA_DROP_FLAG));
146 BigInteger metaDataMask =
147 MetaDataUtil.METADATA_MASK_LPORT_TAG.or(MetaDataUtil.METADATA_MASK_ACL_DROP);
148 matches.add(new MatchMetadata(metaData, metaDataMask));
150 String flowName = "Egress_" + dpId + "_" + lportTag + "_Drop";
151 addFlowEntryToList(flowEntries, dpId, getAclCommitterTable(), flowName,
152 AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, 0, 0, AclServiceUtils.getDropFlowCookie(lportTag),
153 matches, instructions, addOrRemove);
157 protected void programRemoteAclTableFlow(List<FlowEntity> flowEntries, BigInteger dpId, Integer aclTag,
158 AllowedAddressPairs aap, int addOrRemove) {
159 List<MatchInfoBase> flowMatches = new ArrayList<>();
160 flowMatches.addAll(AclServiceUtils.buildIpAndDstServiceMatch(aclTag, aap));
162 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
163 String flowNameAdded = "Acl_Filter_Egress_" + aap.getIpAddress().stringValue() + "_" + aclTag;
165 addFlowEntryToList(flowEntries, dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY,
166 0, 0, AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
170 protected void programGotoClassifierTableRules(List<FlowEntity> flowEntries, BigInteger dpId,
171 List<AllowedAddressPairs> aaps, int lportTag, int addOrRemove) {
172 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(aaps);
173 for (AllowedAddressPairs aap : filteredAAPs) {
174 IpPrefixOrAddress attachIp = aap.getIpAddress();
175 MacAddress mac = aap.getMacAddress();
177 List<MatchInfoBase> matches = new ArrayList<>();
178 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
179 matches.add(new MatchEthernetSource(mac));
180 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_SOURCE));
182 List<InstructionInfo> gotoInstructions = new ArrayList<>();
183 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
185 String flowName = "Egress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
186 + attachIp.stringValue();
187 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
188 AclConstants.PROTO_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions,
194 * Add rule to allow certain ICMPv6 traffic from VM ports.
196 * @param flowEntries the flow entries
197 * @param dpId the dpId
198 * @param lportTag the lport tag
199 * @param addOrRemove add/remove the flow.
201 private void egressAclIcmpv6AllowedList(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag,
203 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
205 for (Integer icmpv6Type: AclConstants.allowedIcmpv6NdList()) {
206 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(icmpv6Type, 0, lportTag, serviceMode);
207 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + icmpv6Type + "_Permit_";
208 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
209 AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
210 instructions, addOrRemove);
215 * Add rule to ensure only DHCP server traffic from the specified mac is allowed.
217 * @param flowEntries the flow entries
218 * @param port the Acl Interface port
219 * @param allowedAddresses the allowed addresses
220 * @param lportTag the lport tag
221 * @param addOrRemove whether to add or remove the flow
223 private void egressAclDhcpAllowClientTraffic(List<FlowEntity> flowEntries, AclInterface port,
224 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
225 // if there is a duplicate mac with different aap, do not delete the Dhcp Allow rule.
226 if (hasDuplicateMac(port.getAllowedAddressPairs(), allowedAddresses)) {
229 BigInteger dpId = port.getDpId();
230 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
231 for (AllowedAddressPairs aap : allowedAddresses) {
232 if (!AclServiceUtils.isIPv4Address(aap)) {
235 List<MatchInfoBase> matches = new ArrayList<>();
236 matches.addAll(AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_CLIENT_PORT_IPV4,
237 AclConstants.DHCP_SERVER_PORT_IPV4, lportTag, serviceMode));
238 matches.add(new MatchEthernetSource(aap.getMacAddress()));
241 "Egress_DHCP_Client_v4" + dpId + "_" + lportTag + "_" + aap.getMacAddress().getValue() + "_Permit_";
242 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
243 AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
244 matches, instructions, addOrRemove);
249 * Add rule to ensure only DHCPv6 server traffic from the specified mac is allowed.
251 * @param flowEntries the flow entries
252 * @param port the Acl Interface port
253 * @param allowedAddresses the allowed addresses
254 * @param lportTag the lport tag
255 * @param addOrRemove whether to add or remove the flow
257 private void egressAclDhcpv6AllowClientTraffic(List<FlowEntity> flowEntries, AclInterface port,
258 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
259 // if there is a duplicate mac with different aap, do not delete the Dhcp Allow rule.
260 if (hasDuplicateMac(port.getAllowedAddressPairs(), allowedAddresses)) {
263 BigInteger dpId = port.getDpId();
264 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
265 for (AllowedAddressPairs aap : allowedAddresses) {
266 if (AclServiceUtils.isIPv4Address(aap)) {
269 List<MatchInfoBase> matches = new ArrayList<>();
270 matches.addAll(AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_CLIENT_PORT_IPV6,
271 AclConstants.DHCP_SERVER_PORT_IPV6, lportTag, serviceMode));
272 matches.add(new MatchEthernetSource(aap.getMacAddress()));
274 String flowName = "Egress_DHCP_Client_v6" + "_" + dpId + "_" + lportTag + "_"
275 + aap.getMacAddress().getValue() + "_Permit_";
276 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
277 AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
278 matches, instructions, addOrRemove);
283 * Adds the rule to allow arp packets.
285 * @param flowEntries the flow entries
286 * @param dpId the dpId
287 * @param allowedAddresses the allowed addresses
288 * @param lportTag the lport tag
289 * @param addOrRemove whether to add or remove the flow
291 protected void programArpRule(List<FlowEntity> flowEntries, BigInteger dpId,
292 List<AllowedAddressPairs> allowedAddresses, int lportTag, int addOrRemove) {
293 for (AllowedAddressPairs allowedAddress : allowedAddresses) {
294 if (!AclServiceUtils.isIPv4Address(allowedAddress)) {
295 continue; // For IPv6 allowed addresses
298 IpPrefixOrAddress allowedAddressIp = allowedAddress.getIpAddress();
299 MacAddress allowedAddressMac = allowedAddress.getMacAddress();
300 List<MatchInfoBase> arpIpMatches = AclServiceUtils.buildArpIpMatches(allowedAddressIp);
301 List<MatchInfoBase> matches = new ArrayList<>();
302 matches.add(MatchEthernetType.ARP);
303 matches.add(new MatchArpSha(allowedAddressMac));
304 matches.add(new MatchEthernetSource(allowedAddressMac));
305 matches.addAll(arpIpMatches);
306 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
308 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
309 LOG.debug("{} ARP Rule on DPID {}, lportTag {}",
310 addOrRemove == NwConstants.DEL_FLOW ? "Deleting" : "Adding", dpId, lportTag);
311 String flowName = "Egress_ARP_" + dpId + "_" + lportTag + "_" + allowedAddress.getMacAddress().getValue()
312 + allowedAddressIp.stringValue();
313 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
314 AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
315 instructions, addOrRemove);
320 protected void programIcmpv6RARule(List<FlowEntity> flowEntries, AclInterface port, List<SubnetInfo> subnets,
322 // No action required on egress.
326 * Programs broadcast rules.
328 * @param flowEntries the flow entries
329 * @param port the Acl Interface port
330 * @param addOrRemove whether to delete or add flow
333 protected void programBroadcastRules(List<FlowEntity> flowEntries, AclInterface port, int addOrRemove) {
334 programL2BroadcastAllowRule(flowEntries, port,
335 AclServiceUtils.excludeMulticastAAPs(port.getAllowedAddressPairs()), addOrRemove);
339 * Programs Non-IP broadcast rules.
341 * @param flowEntries the flow entries
342 * @param port the Acl Interface port
343 * @param filteredAAPs the filtered AAPs list
344 * @param addOrRemove whether to delete or add flow
346 private void programL2BroadcastAllowRule(List<FlowEntity> flowEntries, AclInterface port,
347 List<AllowedAddressPairs> filteredAAPs, int addOrRemove) {
348 // if there is a duplicate mac with different aap, do not delete the Broadcast rule.
349 if (hasDuplicateMac(port.getAllowedAddressPairs(), filteredAAPs)) {
352 BigInteger dpId = port.getDpId();
353 int lportTag = port.getLPortTag();
354 Set<MacAddress> macs = filteredAAPs.stream().map(AllowedAddressPairs::getMacAddress)
355 .collect(Collectors.toSet());
356 for (MacAddress mac : macs) {
357 List<MatchInfoBase> matches = new ArrayList<>();
358 matches.add(new MatchEthernetSource(mac));
359 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
361 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
363 String flowName = "Egress_L2Broadcast_" + dpId + "_" + lportTag + "_" + mac.getValue();
364 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
365 AclConstants.PROTO_L2BROADCAST_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE,
366 matches, instructions, addOrRemove);
370 private boolean hasDuplicateMac(List<AllowedAddressPairs> allowedAddresses,
371 List<AllowedAddressPairs> filteredAAPs) {
372 // Do not proceed further if VM delete or Port down event.
373 if (allowedAddresses.size() == filteredAAPs.size()) {
376 //exclude filteredAAP entries from port's AAP's before comparison
377 List<AllowedAddressPairs> filteredAllowedAddressed = allowedAddresses.stream().filter(
378 aap -> !filteredAAPs.contains(aap)).collect(Collectors.toList());
379 Set<MacAddress> macs = filteredAAPs.stream().map(AllowedAddressPairs::getMacAddress)
380 .collect(Collectors.toSet());
381 List<AllowedAddressPairs> aapWithDuplicateMac = filteredAllowedAddressed.stream()
382 .filter(entry -> macs.contains(entry.getMacAddress())).collect(Collectors.toList());
383 return !aapWithDuplicateMac.isEmpty();
387 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
388 return direction.equals(DirectionEgress.class);
391 private short getAclAntiSpoofingTable() {
392 return NwConstants.INGRESS_ACL_ANTI_SPOOFING_TABLE;
395 private short getAclConntrackClassifierTable() {
396 return NwConstants.INGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
400 protected short getAclConntrackSenderTable() {
401 return NwConstants.INGRESS_ACL_CONNTRACK_SENDER_TABLE;
405 protected short getAclForExistingTrafficTable() {
406 return NwConstants.INGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
410 protected short getAclFilterCumDispatcherTable() {
411 return NwConstants.INGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
415 protected short getAclRuleBasedFilterTable() {
416 return NwConstants.INGRESS_ACL_RULE_BASED_FILTER_TABLE;
420 protected short getAclRemoteAclTable() {
421 return NwConstants.INGRESS_REMOTE_ACL_TABLE;
425 protected short getAclCommitterTable() {
426 return NwConstants.INGRESS_ACL_COMMITTER_TABLE;