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 java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
15 import java.util.stream.Collectors;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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.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.utils.ServiceIndex;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
31 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
32 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
33 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
34 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
35 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
36 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
37 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
51 * Provides the implementation for egress (w.r.t VM) ACL service.
54 * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
57 public class EgressAclServiceImpl extends AbstractAclServiceImpl {
59 private static final Logger LOG = LoggerFactory.getLogger(EgressAclServiceImpl.class);
62 * Initialize the member variables.
64 public EgressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
65 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
66 // Service mode is w.rt. switch
67 super(ServiceModeIngress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils,
68 jobCoordinator, aclInterfaceCache);
74 * @param aclInterface the acl interface
77 public void bindService(AclInterface aclInterface) {
78 String interfaceName = aclInterface.getInterfaceId();
79 jobCoordinator.enqueueJob(interfaceName, () -> {
80 int instructionKey = 0;
81 List<Instruction> instructions = new ArrayList<>();
82 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(getAclAntiSpoofingTable(), ++instructionKey));
83 short serviceIndex = ServiceIndex.getIndex(AclConstants.INGRESS_ACL_SERVICE_NAME,
84 AclConstants.INGRESS_ACL_SERVICE_INDEX);
85 int flowPriority = AclConstants.INGRESS_ACL_SERVICE_INDEX;
86 BoundServices serviceInfo =
87 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "ingressacl", interfaceName),
88 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
89 InstanceIdentifier<BoundServices> path =
90 AclServiceUtils.buildServiceId(interfaceName, serviceIndex, serviceMode);
92 return Collections.singletonList(
93 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
94 path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
101 * @param aclInterface the acl interface
104 protected void unbindService(AclInterface aclInterface) {
105 String interfaceName = aclInterface.getInterfaceId();
106 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
107 ServiceIndex.getIndex(NwConstants.ACL_SERVICE_NAME, NwConstants.ACL_SERVICE_INDEX), serviceMode);
109 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
110 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
111 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
115 protected void programAntiSpoofingRules(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
116 Action action, int addOrRemove) {
117 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
118 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
120 BigInteger dpid = port.getDpId();
121 int lportTag = port.getLPortTag();
122 if (action != Action.UPDATE) {
123 egressAclDhcpDropServerTraffic(dpid, lportTag, addOrRemove);
124 egressAclDhcpv6DropServerTraffic(dpid, lportTag, addOrRemove);
125 egressAclIcmpv6DropRouterAdvts(dpid, lportTag, addOrRemove);
126 egressAclIcmpv6AllowedList(dpid, lportTag, addOrRemove);
127 programL2BroadcastAllowRule(port, addOrRemove);
129 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddresses);
131 egressAclDhcpAllowClientTraffic(dpid, filteredAAPs, lportTag, addOrRemove);
132 egressAclDhcpv6AllowClientTraffic(dpid, filteredAAPs, lportTag, addOrRemove);
133 programArpRule(dpid, filteredAAPs, lportTag, addOrRemove);
137 protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
139 List<MatchInfoBase> flowMatches = new ArrayList<>();
140 flowMatches.addAll(AclServiceUtils.buildIpAndDstServiceMatch(aclTag, aap, dataBroker));
142 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
143 String flowNameAdded = "Acl_Filter_Egress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
145 syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
146 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
150 protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
152 List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(aaps);
153 for (AllowedAddressPairs aap : filteredAAPs) {
154 IpPrefixOrAddress attachIp = aap.getIpAddress();
155 MacAddress mac = aap.getMacAddress();
157 List<MatchInfoBase> matches = new ArrayList<>();
158 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
159 matches.add(new MatchEthernetSource(mac));
160 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_SOURCE));
162 List<InstructionInfo> gotoInstructions = new ArrayList<>();
163 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
165 String flowName = "Egress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
166 + String.valueOf(attachIp.getValue());
167 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
168 AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
173 * Anti-spoofing rule to block the Ipv4 DHCP server traffic from the port.
175 * @param dpId the dpId
176 * @param lportTag the lport tag
177 * @param addOrRemove add/remove the flow.
179 protected void egressAclDhcpDropServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
180 List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
181 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
183 String flowName = "Egress_DHCP_Server_v4" + dpId + "_" + lportTag + "_Drop_";
184 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
185 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
189 * Anti-spoofing rule to block the Ipv6 DHCP server traffic from the port.
191 * @param dpId the dpId
192 * @param lportTag the lport tag
193 * @param addOrRemove add/remove the flow.
195 protected void egressAclDhcpv6DropServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
196 List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
197 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
199 String flowName = "Egress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_Drop_";
200 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
201 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
205 * Anti-spoofing rule to block the Ipv6 Router Advts from the VM port.
207 * @param dpId the dpId
208 * @param lportTag the lport tag
209 * @param addOrRemove add/remove the flow.
211 private void egressAclIcmpv6DropRouterAdvts(BigInteger dpId, int lportTag, int addOrRemove) {
212 List<MatchInfoBase> matches =
213 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0, lportTag, serviceMode);
215 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_RA + "_Drop_";
216 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_DROP_PRIORITY, "ACL", 0, 0,
217 AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
221 * Add rule to allow certain ICMPv6 traffic from VM ports.
223 * @param dpId the dpId
224 * @param lportTag the lport tag
225 * @param addOrRemove add/remove the flow.
227 private void egressAclIcmpv6AllowedList(BigInteger dpId, int lportTag, int addOrRemove) {
228 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
230 for (Integer icmpv6Type: AclConstants.allowedIcmpv6NdList()) {
231 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(icmpv6Type, 0, lportTag, serviceMode);
232 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + icmpv6Type + "_Permit_";
233 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
234 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
239 * Add rule to ensure only DHCP server traffic from the specified mac is allowed.
241 * @param dpId the dpid
242 * @param allowedAddresses the allowed addresses
243 * @param lportTag the lport tag
244 * @param addOrRemove whether to add or remove the flow
246 private void egressAclDhcpAllowClientTraffic(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
247 int lportTag, int addOrRemove) {
248 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
249 for (AllowedAddressPairs aap : allowedAddresses) {
250 if (!AclServiceUtils.isIPv4Address(aap)) {
253 List<MatchInfoBase> matches = new ArrayList<>();
254 matches.addAll(AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_CLIENT_PORT_IPV4,
255 AclConstants.DHCP_SERVER_PORT_IPV4, lportTag, serviceMode));
256 matches.add(new MatchEthernetSource(aap.getMacAddress()));
259 "Egress_DHCP_Client_v4" + dpId + "_" + lportTag + "_" + aap.getMacAddress().getValue() + "_Permit_";
260 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
261 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
266 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
269 * @param dpId the dpid
270 * @param allowedAddresses the allowed addresses
271 * @param lportTag the lport tag
272 * @param addOrRemove whether to add or remove the flow
274 private void egressAclDhcpv6AllowClientTraffic(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
275 int lportTag, int addOrRemove) {
276 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
277 for (AllowedAddressPairs aap : allowedAddresses) {
278 if (AclServiceUtils.isIPv4Address(aap)) {
281 List<MatchInfoBase> matches = new ArrayList<>();
282 matches.addAll(AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_CLIENT_PORT_IPV6,
283 AclConstants.DHCP_SERVER_PORT_IPV6, lportTag, serviceMode));
284 matches.add(new MatchEthernetSource(aap.getMacAddress()));
286 String flowName = "Egress_DHCP_Client_v6" + "_" + dpId + "_" + lportTag + "_"
287 + aap.getMacAddress().getValue() + "_Permit_";
288 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
289 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
294 * Adds the rule to allow arp packets.
296 * @param dpId the dpId
297 * @param allowedAddresses the allowed addresses
298 * @param lportTag the lport tag
299 * @param addOrRemove whether to add or remove the flow
301 protected void programArpRule(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses, int lportTag,
303 for (AllowedAddressPairs allowedAddress : allowedAddresses) {
304 if (!AclServiceUtils.isIPv4Address(allowedAddress)) {
305 continue; // For IPv6 allowed addresses
308 IpPrefixOrAddress allowedAddressIp = allowedAddress.getIpAddress();
309 MacAddress allowedAddressMac = allowedAddress.getMacAddress();
310 List<MatchInfoBase> arpIpMatches = AclServiceUtils.buildArpIpMatches(allowedAddressIp);
311 List<MatchInfoBase> matches = new ArrayList<>();
312 matches.add(MatchEthernetType.ARP);
313 matches.add(new MatchArpSha(allowedAddressMac));
314 matches.add(new MatchEthernetSource(allowedAddressMac));
315 matches.addAll(arpIpMatches);
316 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
318 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
319 LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
320 + "lportTag {}", dpId, lportTag);
321 String flowName = "Egress_ARP_" + dpId + "_" + lportTag + "_" + allowedAddress.getMacAddress().getValue()
322 + String.valueOf(allowedAddressIp.getValue());
323 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0,
324 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
329 * Programs broadcast rules.
331 * @param port the Acl Interface port
332 * @param addOrRemove whether to delete or add flow
335 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
336 programL2BroadcastAllowRule(port, addOrRemove);
340 * Programs Non-IP broadcast rules.
342 * @param port the Acl Interface port
343 * @param addOrRemove whether to delete or add flow
345 private void programL2BroadcastAllowRule(AclInterface port, int addOrRemove) {
346 BigInteger dpId = port.getDpId();
347 int lportTag = port.getLPortTag();
348 List<AllowedAddressPairs> allowedAddresses = port.getAllowedAddressPairs();
349 Set<MacAddress> macs = allowedAddresses.stream().map(aap -> aap.getMacAddress()).collect(Collectors.toSet());
350 for (MacAddress mac : macs) {
351 List<MatchInfoBase> matches = new ArrayList<>();
352 matches.add(new MatchEthernetSource(mac));
353 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
355 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
357 String flowName = "Egress_L2Broadcast_" + dpId + "_" + lportTag + "_" + mac.getValue();
358 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_L2BROADCAST_TRAFFIC_MATCH_PRIORITY,
359 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
364 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
365 return direction.equals(DirectionEgress.class);
369 protected short getAclAntiSpoofingTable() {
370 return NwConstants.INGRESS_ACL_ANTI_SPOOFING_TABLE;
374 protected short getAclConntrackClassifierTable() {
375 return NwConstants.INGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
379 protected short getAclConntrackSenderTable() {
380 return NwConstants.INGRESS_ACL_CONNTRACK_SENDER_TABLE;
384 protected short getAclForExistingTrafficTable() {
385 return NwConstants.INGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
389 protected short getAclFilterCumDispatcherTable() {
390 return NwConstants.INGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
394 protected short getAclRuleBasedFilterTable() {
395 return NwConstants.INGRESS_ACL_RULE_BASED_FILTER_TABLE;
399 protected short getAclRemoteAclTable() {
400 return NwConstants.INGRESS_REMOTE_ACL_TABLE;
404 protected short getAclCommitterTable() {
405 return NwConstants.INGRESS_ACL_COMMITTER_TABLE;