ACL: Handle AAP update
[netvirt.git] / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / EgressAclServiceImpl.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 java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.stream.Collectors;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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.NwConstants;
23 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
24 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
25 import org.opendaylight.genius.mdsalutil.matches.MatchArpSha;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetSource;
27 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
28 import org.opendaylight.genius.utils.ServiceIndex;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.netvirt.aclservice.api.AclInterfaceCache;
31 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.Action;
32 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
33 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
34 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
35 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
36 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
37 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * Provides the implementation for egress (w.r.t VM) ACL service.
52  *
53  * <p>
54  * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
55  * and vice versa.
56  */
57 public class EgressAclServiceImpl extends AbstractAclServiceImpl {
58
59     private static final Logger LOG = LoggerFactory.getLogger(EgressAclServiceImpl.class);
60
61     /**
62      * Initialize the member variables.
63      */
64     public EgressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
65             AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
66         // Service mode is w.rt. switch
67         super(ServiceModeIngress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils,
68                 jobCoordinator, aclInterfaceCache);
69     }
70
71     /**
72      * Bind service.
73      *
74      * @param aclInterface the acl interface
75      */
76     @Override
77     public void bindService(AclInterface aclInterface) {
78         String interfaceName = aclInterface.getInterfaceId();
79         jobCoordinator.enqueueJob(interfaceName, () -> {
80             int instructionKey = 0;
81             List<Instruction> instructions = new ArrayList<>();
82             instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(getAclAntiSpoofingTable(), ++instructionKey));
83             short serviceIndex = ServiceIndex.getIndex(AclConstants.INGRESS_ACL_SERVICE_NAME,
84                     AclConstants.INGRESS_ACL_SERVICE_INDEX);
85             int flowPriority = AclConstants.INGRESS_ACL_SERVICE_INDEX;
86             BoundServices serviceInfo =
87                     AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "ingressacl", interfaceName),
88                             serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
89             InstanceIdentifier<BoundServices> path =
90                     AclServiceUtils.buildServiceId(interfaceName, serviceIndex, serviceMode);
91
92             return Collections.singletonList(
93                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
94                             path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
95         });
96     }
97
98     /**
99      * Unbind service.
100      *
101      * @param aclInterface the acl interface
102      */
103     @Override
104     protected void unbindService(AclInterface aclInterface) {
105         String interfaceName = aclInterface.getInterfaceId();
106         InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
107                 ServiceIndex.getIndex(NwConstants.ACL_SERVICE_NAME, NwConstants.ACL_SERVICE_INDEX), serviceMode);
108
109         LOG.debug("UnBinding ACL service for interface {}", interfaceName);
110         jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
111                 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
112     }
113
114     @Override
115     protected void programAntiSpoofingRules(AclInterface port, List<AllowedAddressPairs> allowedAddresses,
116             Action action, int addOrRemove) {
117         LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
118                 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
119
120         BigInteger dpid = port.getDpId();
121         int lportTag = port.getLPortTag();
122         if (action != Action.UPDATE) {
123             egressAclDhcpDropServerTraffic(dpid, lportTag, addOrRemove);
124             egressAclDhcpv6DropServerTraffic(dpid, lportTag, addOrRemove);
125             egressAclIcmpv6DropRouterAdvts(dpid, lportTag, addOrRemove);
126             egressAclIcmpv6AllowedList(dpid, lportTag, addOrRemove);
127             programL2BroadcastAllowRule(port, addOrRemove);
128         }
129         List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(allowedAddresses);
130
131         egressAclDhcpAllowClientTraffic(dpid, filteredAAPs, lportTag, addOrRemove);
132         egressAclDhcpv6AllowClientTraffic(dpid, filteredAAPs, lportTag, addOrRemove);
133         programArpRule(dpid, filteredAAPs, lportTag, addOrRemove);
134     }
135
136     @Override
137     protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
138             int addOrRemove) {
139         List<MatchInfoBase> flowMatches = new ArrayList<>();
140         flowMatches.addAll(AclServiceUtils.buildIpAndDstServiceMatch(aclTag, aap, dataBroker));
141
142         List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
143         String flowNameAdded = "Acl_Filter_Egress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
144
145         syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
146                 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
147     }
148
149     @Override
150     protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
151             int addOrRemove) {
152         List<AllowedAddressPairs> filteredAAPs = AclServiceUtils.excludeMulticastAAPs(aaps);
153         for (AllowedAddressPairs aap : filteredAAPs) {
154             IpPrefixOrAddress attachIp = aap.getIpAddress();
155             MacAddress mac = aap.getMacAddress();
156
157             List<MatchInfoBase> matches = new ArrayList<>();
158             matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
159             matches.add(new MatchEthernetSource(mac));
160             matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_SOURCE));
161
162             List<InstructionInfo> gotoInstructions = new ArrayList<>();
163             gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
164
165             String flowName = "Egress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
166                     + String.valueOf(attachIp.getValue());
167             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
168                     AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
169         }
170     }
171
172     /**
173      * Anti-spoofing rule to block the Ipv4 DHCP server traffic from the port.
174      *
175      * @param dpId the dpId
176      * @param lportTag the lport tag
177      * @param addOrRemove add/remove the flow.
178      */
179     protected void egressAclDhcpDropServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
180         List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
181                 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
182
183         String flowName = "Egress_DHCP_Server_v4" + dpId + "_" + lportTag + "_Drop_";
184         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
185                 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
186     }
187
188     /**
189      * Anti-spoofing rule to block the Ipv6 DHCP server traffic from the port.
190      *
191      * @param dpId the dpId
192      * @param lportTag the lport tag
193      * @param addOrRemove add/remove the flow.
194      */
195     protected void egressAclDhcpv6DropServerTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
196         List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
197                 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
198
199         String flowName = "Egress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_Drop_";
200         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
201                 "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
202     }
203
204     /**
205      * Anti-spoofing rule to block the Ipv6 Router Advts from the VM port.
206      *
207      * @param dpId the dpId
208      * @param lportTag the lport tag
209      * @param addOrRemove add/remove the flow.
210      */
211     private void egressAclIcmpv6DropRouterAdvts(BigInteger dpId, int lportTag, int addOrRemove) {
212         List<MatchInfoBase> matches =
213                 AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_RA, 0, lportTag, serviceMode);
214
215         String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_RA + "_Drop_";
216         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_DROP_PRIORITY, "ACL", 0, 0,
217                 AclConstants.COOKIE_ACL_BASE, matches, Collections.emptyList(), addOrRemove);
218     }
219
220     /**
221      * Add rule to allow certain ICMPv6 traffic from VM ports.
222      *
223      * @param dpId the dpId
224      * @param lportTag the lport tag
225      * @param addOrRemove add/remove the flow.
226      */
227     private void egressAclIcmpv6AllowedList(BigInteger dpId, int lportTag, int addOrRemove) {
228         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
229
230         for (Integer icmpv6Type: AclConstants.allowedIcmpv6NdList()) {
231             List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(icmpv6Type, 0, lportTag, serviceMode);
232             String flowName = "Egress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + icmpv6Type + "_Permit_";
233             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
234                     AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
235         }
236     }
237
238     /**
239      * Add rule to ensure only DHCP server traffic from the specified mac is allowed.
240      *
241      * @param dpId the dpid
242      * @param allowedAddresses the allowed addresses
243      * @param lportTag the lport tag
244      * @param addOrRemove whether to add or remove the flow
245      */
246     private void egressAclDhcpAllowClientTraffic(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
247             int lportTag, int addOrRemove) {
248         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
249         for (AllowedAddressPairs aap : allowedAddresses) {
250             if (!AclServiceUtils.isIPv4Address(aap)) {
251                 continue;
252             }
253             List<MatchInfoBase> matches = new ArrayList<>();
254             matches.addAll(AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_CLIENT_PORT_IPV4,
255                 AclConstants.DHCP_SERVER_PORT_IPV4, lportTag, serviceMode));
256             matches.add(new MatchEthernetSource(aap.getMacAddress()));
257
258             String flowName =
259                     "Egress_DHCP_Client_v4" + dpId + "_" + lportTag + "_" + aap.getMacAddress().getValue() + "_Permit_";
260             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
261                     "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
262         }
263     }
264
265     /**
266      * Add rule to ensure only DHCPv6 server traffic from the specified mac is
267      * allowed.
268      *
269      * @param dpId the dpid
270      * @param allowedAddresses the allowed addresses
271      * @param lportTag the lport tag
272      * @param addOrRemove whether to add or remove the flow
273      */
274     private void egressAclDhcpv6AllowClientTraffic(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses,
275             int lportTag, int addOrRemove) {
276         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
277         for (AllowedAddressPairs aap : allowedAddresses) {
278             if (AclServiceUtils.isIPv4Address(aap)) {
279                 continue;
280             }
281             List<MatchInfoBase> matches = new ArrayList<>();
282             matches.addAll(AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_CLIENT_PORT_IPV6,
283                 AclConstants.DHCP_SERVER_PORT_IPV6, lportTag, serviceMode));
284             matches.add(new MatchEthernetSource(aap.getMacAddress()));
285
286             String flowName = "Egress_DHCP_Client_v6" + "_" + dpId + "_" + lportTag + "_"
287                     + aap.getMacAddress().getValue() + "_Permit_";
288             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_CLIENT_TRAFFIC_MATCH_PRIORITY,
289                     "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
290         }
291     }
292
293     /**
294      * Adds the rule to allow arp packets.
295      *
296      * @param dpId the dpId
297      * @param allowedAddresses the allowed addresses
298      * @param lportTag the lport tag
299      * @param addOrRemove whether to add or remove the flow
300      */
301     protected void programArpRule(BigInteger dpId, List<AllowedAddressPairs> allowedAddresses, int lportTag,
302             int addOrRemove) {
303         for (AllowedAddressPairs allowedAddress : allowedAddresses) {
304             if (!AclServiceUtils.isIPv4Address(allowedAddress)) {
305                 continue; // For IPv6 allowed addresses
306             }
307
308             IpPrefixOrAddress allowedAddressIp = allowedAddress.getIpAddress();
309             MacAddress allowedAddressMac = allowedAddress.getMacAddress();
310             List<MatchInfoBase> arpIpMatches = AclServiceUtils.buildArpIpMatches(allowedAddressIp);
311             List<MatchInfoBase> matches = new ArrayList<>();
312             matches.add(MatchEthernetType.ARP);
313             matches.add(new MatchArpSha(allowedAddressMac));
314             matches.add(new MatchEthernetSource(allowedAddressMac));
315             matches.addAll(arpIpMatches);
316             matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
317
318             List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
319             LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
320                     + "lportTag {}", dpId, lportTag);
321             String flowName = "Egress_ARP_" + dpId + "_" + lportTag + "_" + allowedAddress.getMacAddress().getValue()
322                     + String.valueOf(allowedAddressIp.getValue());
323             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0,
324                     0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
325         }
326     }
327
328     /**
329      * Programs broadcast rules.
330      *
331      * @param port the Acl Interface port
332      * @param addOrRemove whether to delete or add flow
333      */
334     @Override
335     protected void programBroadcastRules(AclInterface port, int addOrRemove) {
336         programL2BroadcastAllowRule(port, addOrRemove);
337     }
338
339     /**
340      * Programs Non-IP broadcast rules.
341      *
342      * @param port the Acl Interface port
343      * @param addOrRemove whether to delete or add flow
344      */
345     private void programL2BroadcastAllowRule(AclInterface port, int addOrRemove) {
346         BigInteger dpId = port.getDpId();
347         int lportTag = port.getLPortTag();
348         List<AllowedAddressPairs> allowedAddresses = port.getAllowedAddressPairs();
349         Set<MacAddress> macs = allowedAddresses.stream().map(aap -> aap.getMacAddress()).collect(Collectors.toSet());
350         for (MacAddress mac : macs) {
351             List<MatchInfoBase> matches = new ArrayList<>();
352             matches.add(new MatchEthernetSource(mac));
353             matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
354
355             List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
356
357             String flowName = "Egress_L2Broadcast_" + dpId + "_" + lportTag + "_" + mac.getValue();
358             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_L2BROADCAST_TRAFFIC_MATCH_PRIORITY,
359                     "ACL", 0, 0, AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
360         }
361     }
362
363     @Override
364     protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
365         return direction.equals(DirectionEgress.class);
366     }
367
368     @Override
369     protected short getAclAntiSpoofingTable() {
370         return NwConstants.INGRESS_ACL_ANTI_SPOOFING_TABLE;
371     }
372
373     @Override
374     protected short getAclConntrackClassifierTable() {
375         return NwConstants.INGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
376     }
377
378     @Override
379     protected short getAclConntrackSenderTable() {
380         return NwConstants.INGRESS_ACL_CONNTRACK_SENDER_TABLE;
381     }
382
383     @Override
384     protected short getAclForExistingTrafficTable() {
385         return NwConstants.INGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
386     }
387
388     @Override
389     protected short getAclFilterCumDispatcherTable() {
390         return NwConstants.INGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
391     }
392
393     @Override
394     protected short getAclRuleBasedFilterTable() {
395         return NwConstants.INGRESS_ACL_RULE_BASED_FILTER_TABLE;
396     }
397
398     @Override
399     protected short getAclRemoteAclTable() {
400         return NwConstants.INGRESS_REMOTE_ACL_TABLE;
401     }
402
403     @Override
404     protected short getAclCommitterTable() {
405         return NwConstants.INGRESS_ACL_COMMITTER_TABLE;
406     }
407 }