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;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.genius.mdsalutil.InstructionInfo;
18 import org.opendaylight.genius.mdsalutil.MDSALUtil;
19 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
20 import org.opendaylight.genius.mdsalutil.NwConstants;
21 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
22 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
23 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
24 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
25 import org.opendaylight.genius.utils.ServiceIndex;
26 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
27 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
28 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
29 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
30 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
31 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
32 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
33 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
34 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * Provides the implementation for ingress (w.r.t VM) ACL service.
51 * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
54 public class IngressAclServiceImpl extends AbstractAclServiceImpl {
56 private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
59 * Initialize the member variables.
61 * @param dataBroker the data broker instance.
62 * @param mdsalManager the mdsal manager.
63 * @param aclDataUtil the acl data util.
64 * @param aclServiceUtils the acl service util.
65 * @param jobCoordinator the job coordinator
66 * @param aclInterfaceCache the acl interface cache
68 public IngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
69 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
70 // Service mode is w.rt. switch
71 super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator,
78 * @param aclInterface the acl interface
81 public void bindService(AclInterface aclInterface) {
82 String interfaceName = aclInterface.getInterfaceId();
83 jobCoordinator.enqueueJob(interfaceName, () -> {
84 int instructionKey = 0;
85 List<Instruction> instructions = new ArrayList<>();
87 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
88 int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
90 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX);
91 BoundServices serviceInfo =
92 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "egressacl", interfaceName),
93 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
94 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
95 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
98 return Collections.singletonList(
99 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
100 path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
107 * @param aclInterface the acl interface
110 protected void unbindService(AclInterface aclInterface) {
111 String interfaceName = aclInterface.getInterfaceId();
112 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
113 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
116 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
117 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
118 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
122 protected void programAntiSpoofingRules(AclInterface port, String dhcpMacAddress,
123 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
124 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
125 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
127 BigInteger dpid = port.getDpId();
128 int lportTag = port.getLPortTag();
129 if (action == Action.ADD || action == Action.REMOVE) {
130 ingressAclDhcpAllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
131 AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
132 ingressAclDhcpv6AllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
133 AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
134 ingressAclIcmpv6AllowedTraffic(dpid, lportTag, addOrRemove);
136 programArpRule(dpid, lportTag, addOrRemove);
137 programIpv4BroadcastRule(port, addOrRemove);
142 protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
144 for (AllowedAddressPairs aap : aaps) {
145 IpPrefixOrAddress attachIp = aap.getIpAddress();
146 MacAddress mac = aap.getMacAddress();
148 List<MatchInfoBase> matches = new ArrayList<>();
149 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
150 matches.add(new MatchEthernetDestination(mac));
151 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_DESTINATION));
153 List<InstructionInfo> gotoInstructions = new ArrayList<>();
154 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
156 String flowName = "Ingress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
157 + String.valueOf(attachIp.getValue());
158 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
159 AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
164 protected void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag, List<AllowedAddressPairs> deletedAAP,
165 List<AllowedAddressPairs> addedAAP) {
166 // Nothing to do for port update as ingress ARP flow is based only on lportTag
171 protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
173 List<MatchInfoBase> flowMatches = new ArrayList<>();
174 flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap, dataBroker));
176 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
177 String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
179 syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
180 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
184 * Add rule to ensure only DHCP server traffic from the specified mac is
187 * @param dpId the dpid
188 * @param dhcpMacAddress the DHCP server mac address
189 * @param lportTag the lport tag
190 * @param addOrRemove is write or delete
191 * @param protoPortMatchPriority the priority
193 protected void ingressAclDhcpAllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
194 int addOrRemove, int protoPortMatchPriority) {
195 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
196 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
197 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
199 String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_" + dhcpMacAddress + "_Permit_";
200 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
201 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
205 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
208 * @param dpId the dpid
209 * @param dhcpMacAddress the DHCP server mac address
210 * @param lportTag the lport tag
211 * @param addOrRemove is write or delete
212 * @param protoPortMatchPriority the priority
214 protected void ingressAclDhcpv6AllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
215 int addOrRemove, Integer protoPortMatchPriority) {
216 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
217 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
218 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
221 "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_" + "_" + dhcpMacAddress + "_Permit_";
222 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
223 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
227 * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), NS (135), NA (136) are allowed into the VM.
229 * @param dpId the dpid
230 * @param lportTag the lport tag
231 * @param addOrRemove is write or delete
233 private void ingressAclIcmpv6AllowedTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
234 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
236 // Allow ICMPv6 Multicast Listener Query packets.
237 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
238 lportTag, serviceMode);
240 final short tableId = getAclAntiSpoofingTable();
242 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
243 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
244 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
246 // Allow ICMPv6 Neighbor Solicitation packets.
247 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
249 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
250 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
251 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
253 // Allow ICMPv6 Neighbor Advertisement packets.
254 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
256 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
257 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
258 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
262 * Adds the rule to allow arp packets.
264 * @param dpId the dpId
265 * @param lportTag the lport tag
266 * @param addOrRemove whether to add or remove the flow
268 protected void programArpRule(BigInteger dpId, int lportTag, int addOrRemove) {
269 List<MatchInfoBase> matches = new ArrayList<>();
270 matches.add(MatchEthernetType.ARP);
271 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
272 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
273 LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
274 + "lportTag {}", dpId, lportTag);
275 String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
276 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0, 0,
277 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
282 * Programs broadcast rules.
284 * @param port the Acl Interface port
285 * @param addOrRemove whether to delete or add flow
288 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
289 programIpv4BroadcastRule(port, addOrRemove);
293 * Programs IPv4 broadcast rules.
295 * @param port the Acl Interface port
296 * @param addOrRemove whether to delete or add flow
298 private void programIpv4BroadcastRule(AclInterface port, int addOrRemove) {
299 BigInteger dpId = port.getDpId();
300 int lportTag = port.getLPortTag();
301 MatchInfoBase lportMatchInfo = AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode);
302 List<IpPrefixOrAddress> cidrs = port.getSubnetIpPrefixes();
304 List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(cidrs);
305 for (String broadcastAddress : broadcastAddresses) {
306 List<MatchInfoBase> matches =
307 AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
308 matches.add(lportMatchInfo);
309 List<InstructionInfo> instructions = new ArrayList<>();
310 instructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
311 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
312 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
313 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
316 LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
321 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
322 return direction.equals(DirectionIngress.class);
326 protected short getAclAntiSpoofingTable() {
327 return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
331 protected short getAclConntrackClassifierTable() {
332 return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
336 protected short getAclConntrackSenderTable() {
337 return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
341 protected short getAclForExistingTrafficTable() {
342 return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
346 protected short getAclFilterCumDispatcherTable() {
347 return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
351 protected short getAclRuleBasedFilterTable() {
352 return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
356 protected short getAclRemoteAclTable() {
357 return NwConstants.EGRESS_REMOTE_ACL_TABLE;
361 protected short getAclCommitterTable() {
362 return NwConstants.EGRESS_ACL_COMMITTER_TABLE;