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.InstructionInfo;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
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.MatchEthernetDestination;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
27 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
28 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
29 import org.opendaylight.genius.utils.ServiceIndex;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
32 import org.opendaylight.netvirt.aclservice.api.AclServiceManager;
33 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
34 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
35 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
36 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
37 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
38 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
39 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfo;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * Provides the implementation for ingress (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 IngressAclServiceImpl extends AbstractAclServiceImpl {
64 private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
67 * Initialize the member variables.
69 * @param dataBroker the data broker instance.
70 * @param mdsalManager the mdsal manager.
71 * @param aclDataUtil the acl data util.
72 * @param aclServiceUtils the acl service util.
73 * @param jobCoordinator the job coordinator
74 * @param aclInterfaceCache the acl interface cache
76 public IngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
77 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
78 // Service mode is w.rt. switch
79 super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator,
86 * @param aclInterface the acl interface
89 public void bindService(AclInterface aclInterface) {
90 String interfaceName = aclInterface.getInterfaceId();
91 jobCoordinator.enqueueJob(interfaceName, () -> {
92 int instructionKey = 0;
93 List<Instruction> instructions = new ArrayList<>();
95 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
96 int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
98 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX);
99 BoundServices serviceInfo =
100 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "egressacl", interfaceName),
101 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
102 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
103 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
106 return Collections.singletonList(
107 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.put(
108 path, serviceInfo, CREATE_MISSING_PARENTS)));
115 * @param aclInterface the acl interface
118 protected void unbindService(AclInterface aclInterface) {
119 String interfaceName = aclInterface.getInterfaceId();
120 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
121 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
124 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
125 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
126 .callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.delete(path))));
130 protected void programAntiSpoofingRules(AclInterface port,
131 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
132 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
133 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
135 BigInteger dpid = port.getDpId();
136 int lportTag = port.getLPortTag();
137 if (action == Action.ADD || action == Action.REMOVE) {
138 programCommitterDropFlow(dpid, lportTag, addOrRemove);
139 ingressAclDhcpAllowServerTraffic(dpid, lportTag, addOrRemove);
140 ingressAclDhcpv6AllowServerTraffic(dpid, lportTag, addOrRemove);
141 ingressAclIcmpv6AllowedTraffic(port, addOrRemove);
142 programIcmpv6RARule(port, port.getSubnetInfo(), addOrRemove);
144 programArpRule(dpid, lportTag, addOrRemove);
145 programIpv4BroadcastRule(port, addOrRemove);
149 private void programCommitterDropFlow(BigInteger dpId, int lportTag, int addOrRemove) {
150 List<MatchInfoBase> matches = new ArrayList<>();
151 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
153 BigInteger metaData = MetaDataUtil.METADATA_MASK_ACL_DROP
154 .and(AclConstants.METADATA_DROP_FLAG.shiftLeft(2));
155 BigInteger metaDataMask = MetaDataUtil.METADATA_MASK_ACL_DROP
156 .and(AclConstants.METADATA_DROP_FLAG.shiftLeft(2));
157 matches.add(new NxMatchRegister(NxmNxReg6.class, MetaDataUtil.getLportTagForReg6(lportTag).longValue(),
158 MetaDataUtil.getLportTagMaskForReg6()));
159 matches.add(new MatchMetadata(metaData, metaDataMask));
161 String flowName = "Ingress_" + dpId + "_" + lportTag + "_Drop";
162 syncFlow(dpId, getAclCommitterTable(), flowName, AclConstants.CT_STATE_TRACKED_INVALID_PRIORITY,
163 "ACL", 0, 0, AclServiceUtils.getDropFlowCookie(lportTag), matches, instructions, addOrRemove);
167 protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
169 for (AllowedAddressPairs aap : aaps) {
170 IpPrefixOrAddress attachIp = aap.getIpAddress();
171 MacAddress mac = aap.getMacAddress();
173 List<MatchInfoBase> matches = new ArrayList<>();
174 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
175 matches.add(new MatchEthernetDestination(mac));
176 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_DESTINATION));
178 List<InstructionInfo> gotoInstructions = new ArrayList<>();
179 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
181 String flowName = "Ingress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
182 + String.valueOf(attachIp.getValue());
183 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
184 AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
189 protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
191 List<MatchInfoBase> flowMatches = new ArrayList<>();
192 flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap));
194 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
195 String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
197 syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
198 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
202 * Add rule to ensure only DHCP server traffic from the specified mac is
205 * @param dpId the dpid
206 * @param lportTag the lport tag
207 * @param addOrRemove is write or delete
209 protected void ingressAclDhcpAllowServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
210 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
211 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
212 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
214 String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_Permit_";
215 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
216 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
220 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
223 * @param dpId the dpid
224 * @param lportTag the lport tag
225 * @param addOrRemove is write or delete
227 protected void ingressAclDhcpv6AllowServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
228 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
229 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
230 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
232 String flowName = "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_Permit_";
233 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
234 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
238 * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), RS (134), NS (135), NA (136) are
239 * allowed into the VM.
241 * @param port the port
242 * @param addOrRemove is write or delete
244 private void ingressAclIcmpv6AllowedTraffic(AclInterface port, int addOrRemove) {
245 BigInteger dpId = port.getDpId();
246 int lportTag = port.getLPortTag();
247 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
249 // Allow ICMPv6 Multicast Listener Query packets.
250 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
251 lportTag, serviceMode);
253 final short tableId = getAclAntiSpoofingTable();
255 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
256 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
257 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
259 // Allow ICMPv6 Neighbor Solicitation packets.
260 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
262 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
263 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
264 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
266 // Allow ICMPv6 Neighbor Advertisement packets.
267 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
269 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
270 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
271 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
275 protected void programIcmpv6RARule(AclInterface port, List<SubnetInfo> subnets, int addOrRemove) {
276 if (AclServiceUtils.isIpv6Subnet(subnets)) {
277 /* Allow ICMPv6 Router Advertisement packets from external routers as well as internal routers
278 * if subnet is configured with IPv6 version
279 * Allow ICMPv6 Router Advertisement packets if originating from any LinkLocal Address.
281 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
282 List<MatchInfoBase> matches =
283 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0,
284 port.getLPortTag(), serviceMode);
285 matches.addAll(AclServiceUtils.buildIpMatches(
286 new IpPrefixOrAddress(new IpPrefix(AclConstants.IPV6_LINK_LOCAL_PREFIX.toCharArray())),
287 AclServiceManager.MatchCriteria.MATCH_SOURCE));
288 String flowName = "Ingress_ICMPv6" + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
289 + AclConstants.ICMPV6_TYPE_RA + "_LinkLocal_Permit_";
290 syncFlow(port.getDpId(), getAclAntiSpoofingTable(), flowName,
291 AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0,
292 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
297 * Adds the rule to allow arp packets.
299 * @param dpId the dpId
300 * @param lportTag the lport tag
301 * @param addOrRemove whether to add or remove the flow
303 protected void programArpRule(BigInteger dpId, int lportTag, int addOrRemove) {
304 List<MatchInfoBase> matches = new ArrayList<>();
305 matches.add(MatchEthernetType.ARP);
306 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
307 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
308 LOG.debug("{} ARP Rule on DPID {}, lportTag {}", addOrRemove == NwConstants.DEL_FLOW ? "Deleting" : "Adding",
310 String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
311 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0, 0,
312 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
317 * Programs broadcast rules.
319 * @param port the Acl Interface port
320 * @param addOrRemove whether to delete or add flow
323 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
324 programIpv4BroadcastRule(port, addOrRemove);
328 * Programs IPv4 broadcast rules.
330 * @param port the Acl Interface port
331 * @param addOrRemove whether to delete or add flow
333 private void programIpv4BroadcastRule(AclInterface port, int addOrRemove) {
334 BigInteger dpId = port.getDpId();
335 int lportTag = port.getLPortTag();
336 MatchInfoBase lportMatchInfo = AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode);
337 List<SubnetInfo> subnetInfoList = port.getSubnetInfo();
338 if (subnetInfoList != null) {
339 List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(subnetInfoList);
340 for (String broadcastAddress : broadcastAddresses) {
341 List<MatchInfoBase> matches =
342 AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
343 matches.add(lportMatchInfo);
344 List<InstructionInfo> instructions = new ArrayList<>();
345 instructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
346 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
347 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
348 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
351 LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
356 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
357 return direction.equals(DirectionIngress.class);
361 protected short getAclAntiSpoofingTable() {
362 return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
366 protected short getAclConntrackClassifierTable() {
367 return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
371 protected short getAclConntrackSenderTable() {
372 return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
376 protected short getAclForExistingTrafficTable() {
377 return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
381 protected short getAclFilterCumDispatcherTable() {
382 return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
386 protected short getAclRuleBasedFilterTable() {
387 return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
391 protected short getAclRemoteAclTable() {
392 return NwConstants.EGRESS_REMOTE_ACL_TABLE;
396 protected short getAclCommitterTable() {
397 return NwConstants.EGRESS_ACL_COMMITTER_TABLE;