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, String dhcpMacAddress,
116 List<AllowedAddressPairs> allowedAddresses, 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.ADD || action == Action.REMOVE) {
123 Set<MacAddress> aapMacs =
124 allowedAddresses.stream().map(aap -> aap.getMacAddress()).collect(Collectors.toSet());
125 egressAclDhcpAllowClientTraffic(dpid, aapMacs, lportTag, addOrRemove);
126 egressAclDhcpv6AllowClientTraffic(dpid, aapMacs, lportTag, addOrRemove);
127 egressAclDhcpDropServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove);
128 egressAclDhcpv6DropServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove);
129 egressAclIcmpv6DropRouterAdvts(dpid, lportTag, addOrRemove);
130 egressAclIcmpv6AllowedList(dpid, lportTag, addOrRemove);
132 programArpRule(dpid, allowedAddresses, lportTag, addOrRemove);
133 programL2BroadcastAllowRule(port, addOrRemove);
138 protected void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag, List<AllowedAddressPairs> deletedAAP,
139 List<AllowedAddressPairs> addedAAP) {
140 // Remove common allowedAddrPairIPs to avoid delete and add of ARP flows having same MAC and IP
141 deletedAAP.removeAll(addedAAP);
142 programArpRule(dpId, deletedAAP, lportTag, NwConstants.DEL_FLOW);
143 programArpRule(dpId, addedAAP, lportTag, NwConstants.ADD_FLOW);
147 protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
149 List<MatchInfoBase> flowMatches = new ArrayList<>();
150 flowMatches.addAll(AclServiceUtils.buildIpAndDstServiceMatch(aclTag, aap, dataBroker));
152 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
153 String flowNameAdded = "Acl_Filter_Egress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
155 syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
156 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
160 protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
162 for (AllowedAddressPairs aap : aaps) {
163 IpPrefixOrAddress attachIp = aap.getIpAddress();
164 MacAddress mac = aap.getMacAddress();
166 List<MatchInfoBase> matches = new ArrayList<>();
167 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
168 matches.add(new MatchEthernetSource(mac));
169 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_SOURCE));
171 List<InstructionInfo> gotoInstructions = new ArrayList<>();
172 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
174 String flowName = "Egress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
175 + String.valueOf(attachIp.getValue());
176 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
177 AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
182 * Anti-spoofing rule to block the Ipv4 DHCP server traffic from the port.
184 * @param dpId the dpId
185 * @param dhcpMacAddress the Dhcp mac address
186 * @param lportTag the lport tag
187 * @param addOrRemove add/remove the flow.
189 protected void egressAclDhcpDropServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
191 List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
192 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
194 String flowName = "Egress_DHCP_Server_v4" + dpId + "_" + lportTag + "_" + dhcpMacAddress + "_Drop_";
195 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
196 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
200 * Anti-spoofing rule to block the Ipv6 DHCP server traffic from the port.
202 * @param dpId the dpId
203 * @param dhcpMacAddress the Dhcp mac address
204 * @param lportTag the lport tag
205 * @param addOrRemove add/remove the flow.
207 protected void egressAclDhcpv6DropServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
209 List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
210 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
212 String flowName = "Egress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_" + dhcpMacAddress + "_Drop_";
213 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
214 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
218 * Anti-spoofing rule to block the Ipv6 Router Advts from the VM port.
220 * @param dpId the dpId
221 * @param lportTag the lport tag
222 * @param addOrRemove add/remove the flow.
224 private void egressAclIcmpv6DropRouterAdvts(BigInteger dpId, int lportTag, int addOrRemove) {
225 List<MatchInfoBase> matches =
226 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0, lportTag, serviceMode);
228 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_RA + "_Drop_";
229 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_DROP_PRIORITY, "ACL", 0, 0,
230 AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
234 * Add rule to allow certain ICMPv6 traffic from VM ports.
236 * @param dpId the dpId
237 * @param lportTag the lport tag
238 * @param addOrRemove add/remove the flow.
240 private void egressAclIcmpv6AllowedList(BigInteger dpId, int lportTag, int addOrRemove) {
241 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
243 for (Integer icmpv6Type: AclConstants.allowedIcmpv6NdList()) {
244 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(icmpv6Type, 0, lportTag, serviceMode);
245 String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + icmpv6Type + "_Permit_";
246 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
247 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
252 * Add rule to ensure only DHCP server traffic from the specified mac is
255 * @param dpId the dpid
256 * @param aapMacs the AAP mac addresses
257 * @param lportTag the lport tag
258 * @param addOrRemove whether to add or remove the flow
260 private void egressAclDhcpAllowClientTraffic(BigInteger dpId, Set<MacAddress> aapMacs, int lportTag,
262 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
263 for (MacAddress aapMac : aapMacs) {
264 List<MatchInfoBase> matches = new ArrayList<>();
265 matches.addAll(AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_CLIENT_PORT_IPV4,
266 AclConstants.DHCP_SERVER_PORT_IPV4, lportTag, serviceMode));
267 matches.add(new MatchEthernetSource(aapMac));
269 String flowName = "Egress_DHCP_Client_v4" + dpId + "_" + lportTag + "_" + aapMac.getValue() + "_Permit_";
270 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
271 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
276 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
279 * @param dpId the dpid
280 * @param aapMacs the AAP mac addresses
281 * @param lportTag the lport tag
282 * @param addOrRemove whether to add or remove the flow
284 private void egressAclDhcpv6AllowClientTraffic(BigInteger dpId, Set<MacAddress> aapMacs, int lportTag,
286 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
287 for (MacAddress aapMac : aapMacs) {
288 List<MatchInfoBase> matches = new ArrayList<>();
289 matches.addAll(AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_CLIENT_PORT_IPV6,
290 AclConstants.DHCP_SERVER_PORT_IPV6, lportTag, serviceMode));
291 matches.add(new MatchEthernetSource(aapMac));
293 String flowName = "Egress_DHCP_Client_v6" + "_" + dpId + "_" + lportTag + "_" + aapMac.getValue()
295 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
296 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
301 * Adds the rule to allow arp packets.
303 * @param dpId the dpId
304 * @param allowedAddresses the allowed addresses
305 * @param lportTag the lport tag
306 * @param addOrRemove whether to add or remove the flow
308 protected void programArpRule(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses, int lportTag,
310 for (AllowedAddressPairs allowedAddress : allowedAddresses) {
311 if (!AclServiceUtils.isIPv4Address(allowedAddress)) {
312 continue; // For IPv6 allowed addresses
315 IpPrefixOrAddress allowedAddressIp = allowedAddress.getIpAddress();
316 MacAddress allowedAddressMac = allowedAddress.getMacAddress();
317 List<MatchInfoBase> arpIpMatches = AclServiceUtils.buildArpIpMatches(allowedAddressIp);
318 List<MatchInfoBase> matches = new ArrayList<>();
319 matches.add(MatchEthernetType.ARP);
320 matches.add(new MatchArpSha(allowedAddressMac));
321 matches.add(new MatchEthernetSource(allowedAddressMac));
322 matches.addAll(arpIpMatches);
323 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
325 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
326 LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
327 + "lportTag {}", dpId, lportTag);
328 String flowName = "Egress_ARP_" + dpId + "_" + lportTag + "_" + allowedAddress.getMacAddress().getValue()
329 + String.valueOf(allowedAddressIp.getValue());
330 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0,
331 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
336 * Programs broadcast rules.
338 * @param port the Acl Interface port
339 * @param addOrRemove whether to delete or add flow
342 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
343 programL2BroadcastAllowRule(port, addOrRemove);
347 * Programs Non-IP broadcast rules.
349 * @param port the Acl Interface port
350 * @param addOrRemove whether to delete or add flow
352 private void programL2BroadcastAllowRule(AclInterface port, int addOrRemove) {
353 BigInteger dpId = port.getDpId();
354 int lportTag = port.getLPortTag();
355 List<AllowedAddressPairs> allowedAddresses = port.getAllowedAddressPairs();
356 Set<MacAddress> macs = allowedAddresses.stream().map(aap -> aap.getMacAddress()).collect(Collectors.toSet());
357 for (MacAddress mac : macs) {
358 List<MatchInfoBase> matches = new ArrayList<>();
359 matches.add(new MatchEthernetSource(mac));
360 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
362 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
364 String flowName = "Egress_L2Broadcast_" + dpId + "_" + lportTag + "_" + mac.getValue();
365 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_L2BROADCAST_TRAFFIC_MATCH_PRIORITY,
366 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
371 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
372 return direction.equals(DirectionEgress.class);
376 protected short getAclAntiSpoofingTable() {
377 return NwConstants.INGRESS_ACL_ANTI_SPOOFING_TABLE;
381 protected short getAclConntrackClassifierTable() {
382 return NwConstants.INGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
386 protected short getAclConntrackSenderTable() {
387 return NwConstants.INGRESS_ACL_CONNTRACK_SENDER_TABLE;
391 protected short getAclForExistingTrafficTable() {
392 return NwConstants.INGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
396 protected short getAclFilterCumDispatcherTable() {
397 return NwConstants.INGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
401 protected short getAclRuleBasedFilterTable() {
402 return NwConstants.INGRESS_ACL_RULE_BASED_FILTER_TABLE;
406 protected short getAclRemoteAclTable() {
407 return NwConstants.INGRESS_REMOTE_ACL_TABLE;
411 protected short getAclCommitterTable() {
412 return NwConstants.INGRESS_ACL_COMMITTER_TABLE;