Datastore-constrained txes: aclservice
[netvirt.git] / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / IngressAclServiceImpl.java
1 /*
2  * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.aclservice;
9
10 import static org.opendaylight.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12
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;
54
55 /**
56  * Provides the implementation for ingress (w.r.t VM) ACL service.
57  *
58  * <p>
59  * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
60  * and vice versa.
61  */
62 public class IngressAclServiceImpl extends AbstractAclServiceImpl {
63
64     private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
65
66     /**
67      * Initialize the member variables.
68      *
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
75      */
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,
80                 aclInterfaceCache);
81     }
82
83     /**
84      * Bind service.
85      *
86      * @param aclInterface the acl interface
87      */
88     @Override
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<>();
94             instructions.add(
95                     MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
96             int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
97             short serviceIndex =
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),
104                     serviceMode);
105
106             return Collections.singletonList(
107                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.put(
108                             path, serviceInfo, CREATE_MISSING_PARENTS)));
109         });
110     }
111
112     /**
113      * Unbind service.
114      *
115      * @param aclInterface the acl interface
116      */
117     @Override
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),
122                 serviceMode);
123
124         LOG.debug("UnBinding ACL service for interface {}", interfaceName);
125         jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
126                 .callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> tx.delete(path))));
127     }
128
129     @Override
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);
134
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);
143
144             programArpRule(dpid, lportTag, addOrRemove);
145             programIpv4BroadcastRule(port, addOrRemove);
146         }
147     }
148
149     private void programCommitterDropFlow(BigInteger dpId, int lportTag, int addOrRemove) {
150         List<MatchInfoBase> matches = new ArrayList<>();
151         List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getDropInstructionInfo();
152
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));
160
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);
164     }
165
166     @Override
167     protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
168             int addOrRemove) {
169         for (AllowedAddressPairs aap : aaps) {
170             IpPrefixOrAddress attachIp = aap.getIpAddress();
171             MacAddress mac = aap.getMacAddress();
172
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));
177
178             List<InstructionInfo> gotoInstructions = new ArrayList<>();
179             gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
180
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);
185         }
186     }
187
188     @Override
189     protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
190             int addOrRemove) {
191         List<MatchInfoBase> flowMatches = new ArrayList<>();
192         flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap));
193
194         List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
195         String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
196
197         syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
198                 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
199     }
200
201     /**
202      * Add rule to ensure only DHCP server traffic from the specified mac is
203      * allowed.
204      *
205      * @param dpId the dpid
206      * @param lportTag the lport tag
207      * @param addOrRemove is write or delete
208      */
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();
213
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);
217     }
218
219     /**
220      * Add rule to ensure only DHCPv6 server traffic from the specified mac is
221      * allowed.
222      *
223      * @param dpId the dpid
224      * @param lportTag the lport tag
225      * @param addOrRemove is write or delete
226      */
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();
231
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);
235     }
236
237     /**
238      * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), RS (134), NS (135), NA (136) are
239      * allowed into the VM.
240      *
241      * @param port the port
242      * @param addOrRemove is write or delete
243      */
244     private void ingressAclIcmpv6AllowedTraffic(AclInterface port, int addOrRemove) {
245         BigInteger dpId = port.getDpId();
246         int lportTag = port.getLPortTag();
247         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
248
249         // Allow ICMPv6 Multicast Listener Query packets.
250         List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
251                 lportTag, serviceMode);
252
253         final short tableId = getAclAntiSpoofingTable();
254         String flowName =
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);
258
259         // Allow ICMPv6 Neighbor Solicitation packets.
260         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
261
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);
265
266         // Allow ICMPv6 Neighbor Advertisement packets.
267         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
268
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);
272     }
273
274     @Override
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.
280              */
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);
293         }
294     }
295
296     /**
297      * Adds the rule to allow arp packets.
298      *
299      * @param dpId the dpId
300      * @param lportTag the lport tag
301      * @param addOrRemove whether to add or remove the flow
302      */
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",
309                 dpId, lportTag);
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);
313     }
314
315
316     /**
317      * Programs broadcast rules.
318      *
319      * @param port the Acl Interface port
320      * @param addOrRemove whether to delete or add flow
321      */
322     @Override
323     protected void programBroadcastRules(AclInterface port, int addOrRemove) {
324         programIpv4BroadcastRule(port, addOrRemove);
325     }
326
327     /**
328      * Programs IPv4 broadcast rules.
329      *
330      * @param port the Acl Interface port
331      * @param addOrRemove whether to delete or add flow
332      */
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);
349             }
350         } else {
351             LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
352         }
353     }
354
355     @Override
356     protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
357         return direction.equals(DirectionIngress.class);
358     }
359
360     @Override
361     protected short getAclAntiSpoofingTable() {
362         return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
363     }
364
365     @Override
366     protected short getAclConntrackClassifierTable() {
367         return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
368     }
369
370     @Override
371     protected short getAclConntrackSenderTable() {
372         return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
373     }
374
375     @Override
376     protected short getAclForExistingTrafficTable() {
377         return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
378     }
379
380     @Override
381     protected short getAclFilterCumDispatcherTable() {
382         return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
383     }
384
385     @Override
386     protected short getAclRuleBasedFilterTable() {
387         return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
388     }
389
390     @Override
391     protected short getAclRemoteAclTable() {
392         return NwConstants.EGRESS_REMOTE_ACL_TABLE;
393     }
394
395     @Override
396     protected short getAclCommitterTable() {
397         return NwConstants.EGRESS_ACL_COMMITTER_TABLE;
398     }
399 }