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;
29 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
30 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
31 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
32 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
33 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
34 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
35 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.port.subnets.port.subnet.SubnetInfo;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
51 * Provides the implementation for ingress (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 IngressAclServiceImpl extends AbstractAclServiceImpl {
59 private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
62 * Initialize the member variables.
64 * @param dataBroker the data broker instance.
65 * @param mdsalManager the mdsal manager.
66 * @param aclDataUtil the acl data util.
67 * @param aclServiceUtils the acl service util.
68 * @param jobCoordinator the job coordinator
69 * @param aclInterfaceCache the acl interface cache
71 public IngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
72 AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
73 // Service mode is w.rt. switch
74 super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator,
81 * @param aclInterface the acl interface
84 public void bindService(AclInterface aclInterface) {
85 String interfaceName = aclInterface.getInterfaceId();
86 jobCoordinator.enqueueJob(interfaceName, () -> {
87 int instructionKey = 0;
88 List<Instruction> instructions = new ArrayList<>();
90 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
91 int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
93 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX);
94 BoundServices serviceInfo =
95 AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "egressacl", interfaceName),
96 serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
97 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
98 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
101 return Collections.singletonList(
102 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
103 path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
110 * @param aclInterface the acl interface
113 protected void unbindService(AclInterface aclInterface) {
114 String interfaceName = aclInterface.getInterfaceId();
115 InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
116 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
119 LOG.debug("UnBinding ACL service for interface {}", interfaceName);
120 jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
121 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
125 protected void programAntiSpoofingRules(AclInterface port,
126 List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
127 LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
128 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
130 BigInteger dpid = port.getDpId();
131 int lportTag = port.getLPortTag();
132 if (action == Action.ADD || action == Action.REMOVE) {
133 ingressAclDhcpAllowServerTraffic(dpid, lportTag, addOrRemove);
134 ingressAclDhcpv6AllowServerTraffic(dpid, lportTag, addOrRemove);
135 ingressAclIcmpv6AllowedTraffic(port, addOrRemove);
136 programIcmpv6RARule(port, port.getSubnetInfo(), addOrRemove);
138 programArpRule(dpid, lportTag, addOrRemove);
139 programIpv4BroadcastRule(port, addOrRemove);
144 protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
146 for (AllowedAddressPairs aap : aaps) {
147 IpPrefixOrAddress attachIp = aap.getIpAddress();
148 MacAddress mac = aap.getMacAddress();
150 List<MatchInfoBase> matches = new ArrayList<>();
151 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
152 matches.add(new MatchEthernetDestination(mac));
153 matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_DESTINATION));
155 List<InstructionInfo> gotoInstructions = new ArrayList<>();
156 gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
158 String flowName = "Ingress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
159 + String.valueOf(attachIp.getValue());
160 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
161 AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
166 protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
168 List<MatchInfoBase> flowMatches = new ArrayList<>();
169 flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap));
171 List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
172 String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
174 syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
175 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
179 * Add rule to ensure only DHCP server traffic from the specified mac is
182 * @param dpId the dpid
183 * @param lportTag the lport tag
184 * @param addOrRemove is write or delete
186 protected void ingressAclDhcpAllowServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
187 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
188 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
189 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
191 String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_Permit_";
192 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
193 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
197 * Add rule to ensure only DHCPv6 server traffic from the specified mac is
200 * @param dpId the dpid
201 * @param lportTag the lport tag
202 * @param addOrRemove is write or delete
204 protected void ingressAclDhcpv6AllowServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
205 final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
206 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
207 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
209 String flowName = "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_Permit_";
210 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
211 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
215 * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), RS (134), NS (135), NA (136) are
216 * allowed into the VM.
218 * @param port the port
219 * @param addOrRemove is write or delete
221 private void ingressAclIcmpv6AllowedTraffic(AclInterface port, int addOrRemove) {
222 BigInteger dpId = port.getDpId();
223 int lportTag = port.getLPortTag();
224 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
226 // Allow ICMPv6 Multicast Listener Query packets.
227 List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
228 lportTag, serviceMode);
230 final short tableId = getAclAntiSpoofingTable();
232 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
233 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
234 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
236 // Allow ICMPv6 Neighbor Solicitation packets.
237 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
239 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
240 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
241 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
243 // Allow ICMPv6 Neighbor Advertisement packets.
244 matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
246 flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
247 syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
248 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
252 protected void programIcmpv6RARule(AclInterface port, List<SubnetInfo> subnets, int addOrRemove) {
253 // Allow ICMPv6 Router Advertisement packets from external routers only if ipv6_ra_mode is not
254 // specified for an IPv6 subnet.
255 if (!AclServiceUtils.isIpv6RaAllowedFromExternalRouters(subnets)) {
258 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
259 List<MatchInfoBase> matches =
260 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0, port.getLPortTag(), serviceMode);
261 // Allow ICMPv6 Router Advertisement packets if originating from any LinkLocal Address.
262 matches.addAll(AclServiceUtils.buildIpMatches(
263 new IpPrefixOrAddress(new IpPrefix(AclConstants.IPV6_LINK_LOCAL_PREFIX.toCharArray())),
264 AclServiceManager.MatchCriteria.MATCH_SOURCE));
266 String flowName = "Ingress_ICMPv6" + "_" + port.getDpId() + "_" + port.getLPortTag() + "_"
267 + AclConstants.ICMPV6_TYPE_RA + "_LinkLocal_Permit_";
268 syncFlow(port.getDpId(), getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL",
269 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
273 * Adds the rule to allow arp packets.
275 * @param dpId the dpId
276 * @param lportTag the lport tag
277 * @param addOrRemove whether to add or remove the flow
279 protected void programArpRule(BigInteger dpId, int lportTag, int addOrRemove) {
280 List<MatchInfoBase> matches = new ArrayList<>();
281 matches.add(MatchEthernetType.ARP);
282 matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
283 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
284 LOG.debug("{} ARP Rule on DPID {}, lportTag {}", addOrRemove == NwConstants.DEL_FLOW ? "Deleting" : "Adding",
286 String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
287 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0, 0,
288 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
293 * Programs broadcast rules.
295 * @param port the Acl Interface port
296 * @param addOrRemove whether to delete or add flow
299 protected void programBroadcastRules(AclInterface port, int addOrRemove) {
300 programIpv4BroadcastRule(port, addOrRemove);
304 * Programs IPv4 broadcast rules.
306 * @param port the Acl Interface port
307 * @param addOrRemove whether to delete or add flow
309 private void programIpv4BroadcastRule(AclInterface port, int addOrRemove) {
310 BigInteger dpId = port.getDpId();
311 int lportTag = port.getLPortTag();
312 MatchInfoBase lportMatchInfo = AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode);
313 List<SubnetInfo> subnetInfoList = port.getSubnetInfo();
314 if (subnetInfoList != null) {
315 List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(subnetInfoList);
316 for (String broadcastAddress : broadcastAddresses) {
317 List<MatchInfoBase> matches =
318 AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
319 matches.add(lportMatchInfo);
320 List<InstructionInfo> instructions = new ArrayList<>();
321 instructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
322 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
323 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
324 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
327 LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
332 protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
333 return direction.equals(DirectionIngress.class);
337 protected short getAclAntiSpoofingTable() {
338 return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
342 protected short getAclConntrackClassifierTable() {
343 return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
347 protected short getAclConntrackSenderTable() {
348 return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
352 protected short getAclForExistingTrafficTable() {
353 return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
357 protected short getAclFilterCumDispatcherTable() {
358 return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
362 protected short getAclRuleBasedFilterTable() {
363 return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
367 protected short getAclRemoteAclTable() {
368 return NwConstants.EGRESS_REMOTE_ACL_TABLE;
372 protected short getAclCommitterTable() {
373 return NwConstants.EGRESS_ACL_COMMITTER_TABLE;