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;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.genius.mdsalutil.FlowEntity;
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.MetaDataUtil;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
25 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
27 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
28 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
29 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
30 import org.opendaylight.genius.utils.ServiceIndex;
31 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
32 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
33 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
34 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
35 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
36 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
37 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
38 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
39 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
40 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefixBuilder;
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.ServiceModeEgress;
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.DirectionIngress;
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.interfaces._interface.SubnetInfo;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Provides the implementation for ingress (w.r.t VM) ACL service.
60 * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
63 public class IngressAclServiceImpl extends AbstractAclServiceImpl {
65 private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
68 * Initialize the member variables.
70 * @param dataBroker the data broker instance.
71 * @param mdsalManager the mdsal manager.
72 * @param aclDataUtil the acl data util.
73 * @param aclServiceUtils the acl service util.
74 * @param jobCoordinator the job coordinator
75 * @param aclInterfaceCache the acl interface cache
77 public IngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
78 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
79 // Service mode is w.rt. switch
80 super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator,
87 * @param aclInterface the acl interface
90 public void bindService(AclInterface aclInterface) {
91 String interfaceName = aclInterface.getInterfaceId();
92 jobCoordinator.enqueueJob(interfaceName, () -> {
93 int instructionKey = 0;
94 List<Instruction> instructions = new ArrayList<>();
96 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
97 int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
99 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX);
100 BoundServices serviceInfo =
101 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "egressacl", interfaceName),
102 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
103 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
104 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
107 return Collections.singletonList(
108 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.put(
109 path, serviceInfo, CREATE_MISSING_PARENTS)));
116 * @param aclInterface the acl interface
119 protected void unbindService(AclInterface aclInterface) {
120 String interfaceName = aclInterface.getInterfaceId();
121 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
122 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
125 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
126 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
127 .callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.delete(path))));
131 protected void programAntiSpoofingRules(List<FlowEntity> flowEntries, AclInterface port,
132 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
133 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
134 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
136 BigInteger dpid = port.getDpId();
137 int lportTag = port.getLPortTag();
138 if (action == Action.ADD || action == Action.REMOVE) {
139 programCommitterDropFlow(flowEntries, dpid, lportTag, addOrRemove);
140 ingressAclDhcpAllowServerTraffic(flowEntries, dpid, lportTag, addOrRemove);
141 ingressAclDhcpv6AllowServerTraffic(flowEntries, dpid, lportTag, addOrRemove);
142 ingressAclIcmpv6AllowedTraffic(flowEntries, port, addOrRemove);
143 programIcmpv6RARule(flowEntries, port, port.getSubnetInfo(), addOrRemove);
145 programArpRule(flowEntries, dpid, lportTag, addOrRemove);
146 programIpv4BroadcastRule(flowEntries, port, port.getSubnetInfo(), addOrRemove);
150 private void programCommitterDropFlow(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag,
152 List<MatchInfoBase> matches = new ArrayList<>();
153 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
155 BigInteger metaData = MetaDataUtil.METADATA_MASK_ACL_DROP
156 .and(AclConstants.METADATA_DROP_FLAG.shiftLeft(2));
157 BigInteger metaDataMask = MetaDataUtil.METADATA_MASK_ACL_DROP
158 .and(AclConstants.METADATA_DROP_FLAG.shiftLeft(2));
159 matches.add(new NxMatchRegister(NxmNxReg6.class, MetaDataUtil.getLportTagForReg6(lportTag).longValue(),
160 MetaDataUtil.getLportTagMaskForReg6()));
161 matches.add(new MatchMetadata(metaData, metaDataMask));
163 String flowName = "Ingress_" + dpId + "_" + lportTag + "_Drop";
164 addFlowEntryToList(flowEntries, dpId, getAclCommitterTable(), flowName,
165 AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY, 0, 0, AclServiceUtils.getDropFlowCookie(lportTag),
166 matches, instructions, addOrRemove);
170 protected void programGotoClassifierTableRules(List<FlowEntity> flowEntries, BigInteger dpId,
171 List<AllowedAddressPairs> aaps, int lportTag, int addOrRemove) {
172 for (AllowedAddressPairs aap : aaps) {
173 IpPrefixOrAddress attachIp = aap.getIpAddress();
174 MacAddress mac = aap.getMacAddress();
176 List<MatchInfoBase> matches = new ArrayList<>();
177 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
178 matches.add(new MatchEthernetDestination(mac));
179 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_DESTINATION));
181 List<InstructionInfo> gotoInstructions = new ArrayList<>();
182 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
184 String flowName = "Ingress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
185 + attachIp.stringValue();
186 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
187 AclConstants.PROTO_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions,
193 protected void programRemoteAclTableFlow(List<FlowEntity> flowEntries, BigInteger dpId, Integer aclTag,
194 AllowedAddressPairs aap, int addOrRemove) {
195 List<MatchInfoBase> flowMatches = new ArrayList<>();
196 flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap));
198 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
199 String flowNameAdded = "Acl_Filter_Ingress_" + aap.getIpAddress().stringValue() + "_" + aclTag;
201 addFlowEntryToList(flowEntries, dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY,
202 0, 0, AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
206 * Add rule to ensure only DHCP server traffic from the specified mac is allowed.
208 * @param flowEntries the flow entries
209 * @param dpId the dpid
210 * @param lportTag the lport tag
211 * @param addOrRemove is write or delete
213 protected void ingressAclDhcpAllowServerTraffic(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag,
215 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
216 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
217 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
219 String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_Permit_";
220 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
221 AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
222 instructions, addOrRemove);
226 * Add rule to ensure only DHCPv6 server traffic from the specified mac is allowed.
228 * @param flowEntries the flow entries
229 * @param dpId the dpid
230 * @param lportTag the lport tag
231 * @param addOrRemove is write or delete
233 protected void ingressAclDhcpv6AllowServerTraffic(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag,
235 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
236 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
237 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
239 String flowName = "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_Permit_";
240 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
241 AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
242 instructions, addOrRemove);
246 * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), RS (134), NS (135), NA (136) are
247 * allowed into the VM.
249 * @param flowEntries the flow entries
250 * @param port the port
251 * @param addOrRemove is write or delete
253 private void ingressAclIcmpv6AllowedTraffic(List<FlowEntity> flowEntries, AclInterface port, int addOrRemove) {
254 BigInteger dpId = port.getDpId();
255 int lportTag = port.getLPortTag();
256 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
258 // Allow ICMPv6 Multicast Listener Query packets.
259 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
260 lportTag, serviceMode);
262 final short tableId = getAclAntiSpoofingTable();
264 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
265 addFlowEntryToList(flowEntries, dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0,
266 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
268 // Allow ICMPv6 Neighbor Solicitation packets.
269 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
271 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
272 addFlowEntryToList(flowEntries, dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0,
273 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
275 // Allow ICMPv6 Neighbor Advertisement packets.
276 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
278 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
279 addFlowEntryToList(flowEntries, dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0,
280 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
284 protected void programIcmpv6RARule(List<FlowEntity> flowEntries, AclInterface port, List<SubnetInfo> subnets,
286 if (AclServiceUtils.isIpv6Subnet(subnets)) {
287 /* Allow ICMPv6 Router Advertisement packets from external routers as well as internal routers
288 * if subnet is configured with IPv6 version
289 * Allow ICMPv6 Router Advertisement packets if originating from any LinkLocal Address.
291 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
292 List<MatchInfoBase> matches =
293 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0,
294 port.getLPortTag(), serviceMode);
295 matches.addAll(AclServiceUtils.buildIpMatches(
296 new IpPrefixOrAddress(IpPrefixBuilder.getDefaultInstance(AclConstants.IPV6_LINK_LOCAL_PREFIX)),
297 AclServiceManager.MatchCriteria.MATCH_SOURCE));
298 String flowName = "Ingress_ICMPv6" + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
299 + AclConstants.ICMPV6_TYPE_RA + "_LinkLocal_Permit_";
300 addFlowEntryToList(flowEntries, port.getDpId(), getAclAntiSpoofingTable(), flowName,
301 AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
302 instructions, addOrRemove);
307 * Adds the rule to allow arp packets.
309 * @param flowEntries the flow entries
310 * @param dpId the dpId
311 * @param lportTag the lport tag
312 * @param addOrRemove whether to add or remove the flow
314 protected void programArpRule(List<FlowEntity> flowEntries, BigInteger dpId, int lportTag, int addOrRemove) {
315 List<MatchInfoBase> matches = new ArrayList<>();
316 matches.add(MatchEthernetType.ARP);
317 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
318 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
319 LOG.debug("{} ARP Rule on DPID {}, lportTag {}", addOrRemove == NwConstants.DEL_FLOW ? "Deleting" : "Adding",
321 String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
322 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
323 AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches,
324 instructions, addOrRemove);
329 * Programs broadcast rules.
331 * @param flowEntries the flow entries
332 * @param port the Acl Interface port
333 * @param addOrRemove whether to delete or add flow
336 protected void programBroadcastRules(List<FlowEntity> flowEntries, AclInterface port, int addOrRemove) {
337 programIpv4BroadcastRule(flowEntries, port, port.getSubnetInfo(), addOrRemove);
341 * Programs broadcast rules.
343 * @param flowEntries the flow entries
344 * @param port the Acl Interface port
345 * @param subnetInfoList the port subnet info list
346 * @param addOrRemove whether to delete or add flow
348 protected void programSubnetBroadcastRules(List<FlowEntity> flowEntries, AclInterface port,
349 List<SubnetInfo> subnetInfoList, int addOrRemove) {
350 programIpv4BroadcastRule(flowEntries, port, subnetInfoList, addOrRemove);
354 * Programs IPv4 broadcast rules.
356 * @param flowEntries the flow entries
357 * @param port the Acl Interface port
358 * @param subnetInfoList Port subnet list
359 * @param addOrRemove whether to delete or add flow
361 private void programIpv4BroadcastRule(List<FlowEntity> flowEntries, AclInterface port,
362 List<SubnetInfo> subnetInfoList, int addOrRemove) {
363 BigInteger dpId = port.getDpId();
364 int lportTag = port.getLPortTag();
365 MatchInfoBase lportMatchInfo = AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode);
366 if (subnetInfoList != null) {
367 List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(subnetInfoList);
368 for (String broadcastAddress : broadcastAddresses) {
369 List<MatchInfoBase> matches =
370 AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
371 matches.add(lportMatchInfo);
372 List<InstructionInfo> instructions = new ArrayList<>();
373 instructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
374 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
375 addFlowEntryToList(flowEntries, dpId, getAclAntiSpoofingTable(), flowName,
376 AclConstants.PROTO_MATCH_PRIORITY, 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions,
380 LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
385 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
386 return direction.equals(DirectionIngress.class);
389 private short getAclAntiSpoofingTable() {
390 return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
393 private short getAclConntrackClassifierTable() {
394 return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
398 protected short getAclConntrackSenderTable() {
399 return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
403 protected short getAclForExistingTrafficTable() {
404 return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
408 protected short getAclFilterCumDispatcherTable() {
409 return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
413 protected short getAclRuleBasedFilterTable() {
414 return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
418 protected short getAclRemoteAclTable() {
419 return NwConstants.EGRESS_REMOTE_ACL_TABLE;
423 protected short getAclCommitterTable() {
424 return NwConstants.EGRESS_ACL_COMMITTER_TABLE;