5e65973d0e01330e4206b5cf2d22664d1129f7c7
[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 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.Action;
29 import org.opendaylight.netvirt.aclservice.api.AclServiceManager.MatchCriteria;
30 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
31 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
32 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
33 import org.opendaylight.netvirt.aclservice.utils.AclServiceOFFlowBuilder;
34 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionIngress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * Provides the implementation for ingress (w.r.t VM) ACL service.
49  *
50  * <p>
51  * Note: Table names used are w.r.t switch. Hence, switch ingress is VM egress
52  * and vice versa.
53  */
54 public class IngressAclServiceImpl extends AbstractAclServiceImpl {
55
56     private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
57
58     /**
59      * Initialize the member variables.
60      *
61      * @param dataBroker the data broker instance.
62      * @param mdsalManager the mdsal manager.
63      * @param aclDataUtil the acl data util.
64      * @param aclServiceUtils the acl service util.
65      * @param jobCoordinator the job coordinator
66      * @param aclInterfaceCache the acl interface cache
67      */
68     public IngressAclServiceImpl(DataBroker dataBroker, IMdsalApiManager mdsalManager, AclDataUtil aclDataUtil,
69             AclServiceUtils aclServiceUtils, JobCoordinator jobCoordinator, AclInterfaceCache aclInterfaceCache) {
70         // Service mode is w.rt. switch
71         super(ServiceModeEgress.class, dataBroker, mdsalManager, aclDataUtil, aclServiceUtils, jobCoordinator,
72                 aclInterfaceCache);
73     }
74
75     /**
76      * Bind service.
77      *
78      * @param aclInterface the acl interface
79      */
80     @Override
81     public void bindService(AclInterface aclInterface) {
82         String interfaceName = aclInterface.getInterfaceId();
83         jobCoordinator.enqueueJob(interfaceName, () -> {
84             int instructionKey = 0;
85             List<Instruction> instructions = new ArrayList<>();
86             instructions.add(
87                     MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
88             int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
89             short serviceIndex =
90                     ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX);
91             BoundServices serviceInfo =
92                     AclServiceUtils.getBoundServices(String.format("%s.%s.%s", "acl", "egressacl", interfaceName),
93                             serviceIndex, flowPriority, AclConstants.COOKIE_ACL_BASE, instructions);
94             InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
95                     ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
96                     serviceMode);
97
98             return Collections.singletonList(
99                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
100                             path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
101         });
102     }
103
104     /**
105      * Unbind service.
106      *
107      * @param aclInterface the acl interface
108      */
109     @Override
110     protected void unbindService(AclInterface aclInterface) {
111         String interfaceName = aclInterface.getInterfaceId();
112         InstanceIdentifier<BoundServices> path = AclServiceUtils.buildServiceId(interfaceName,
113                 ServiceIndex.getIndex(NwConstants.EGRESS_ACL_SERVICE_NAME, NwConstants.EGRESS_ACL_SERVICE_INDEX),
114                 serviceMode);
115
116         LOG.debug("UnBinding ACL service for interface {}", interfaceName);
117         jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
118                 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
119     }
120
121     @Override
122     protected void programAntiSpoofingRules(AclInterface port, String dhcpMacAddress,
123             List<AllowedAddressPairs> allowedAddresses, Action action, int addOrRemove) {
124         LOG.info("{} programAntiSpoofingRules for port {}, AAPs={}, action={}, addOrRemove={}", this.directionString,
125                 port.getInterfaceId(), allowedAddresses, action, addOrRemove);
126
127         BigInteger dpid = port.getDpId();
128         int lportTag = port.getLPortTag();
129         if (action == Action.ADD || action == Action.REMOVE) {
130             ingressAclDhcpAllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
131                     AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
132             ingressAclDhcpv6AllowServerTraffic(dpid, dhcpMacAddress, lportTag, addOrRemove,
133                     AclConstants.PROTO_PREFIX_MATCH_PRIORITY);
134             ingressAclIcmpv6AllowedTraffic(dpid, lportTag, addOrRemove);
135
136             programArpRule(dpid, lportTag, addOrRemove);
137             programIpv4BroadcastRule(port, addOrRemove);
138         }
139     }
140
141     @Override
142     protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
143             int addOrRemove) {
144         for (AllowedAddressPairs aap : aaps) {
145             IpPrefixOrAddress attachIp = aap.getIpAddress();
146             MacAddress mac = aap.getMacAddress();
147
148             List<MatchInfoBase> matches = new ArrayList<>();
149             matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
150             matches.add(new MatchEthernetDestination(mac));
151             matches.addAll(AclServiceUtils.buildIpMatches(attachIp, MatchCriteria.MATCH_DESTINATION));
152
153             List<InstructionInfo> gotoInstructions = new ArrayList<>();
154             gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
155
156             String flowName = "Ingress_Fixed_Goto_Classifier_" + dpId + "_" + lportTag + "_" + mac.getValue() + "_"
157                     + String.valueOf(attachIp.getValue());
158             syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
159                     AclConstants.COOKIE_ACL_BASE, matches, gotoInstructions, addOrRemove);
160         }
161     }
162
163     @Override
164     protected void updateArpForAllowedAddressPairs(BigInteger dpId, int lportTag, List<AllowedAddressPairs> deletedAAP,
165             List<AllowedAddressPairs> addedAAP) {
166         // Nothing to do for port update as ingress ARP flow is based only on lportTag
167
168     }
169
170     @Override
171     protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
172             int addOrRemove) {
173         List<MatchInfoBase> flowMatches = new ArrayList<>();
174         flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap, dataBroker));
175
176         List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
177         String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
178
179         syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
180                 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
181     }
182
183     /**
184      * Add rule to ensure only DHCP server traffic from the specified mac is
185      * allowed.
186      *
187      * @param dpId the dpid
188      * @param dhcpMacAddress the DHCP server mac address
189      * @param lportTag the lport tag
190      * @param addOrRemove is write or delete
191      * @param protoPortMatchPriority the priority
192      */
193     protected void ingressAclDhcpAllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
194             int addOrRemove, int protoPortMatchPriority) {
195         final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpMatches(AclConstants.DHCP_SERVER_PORT_IPV4,
196                 AclConstants.DHCP_CLIENT_PORT_IPV4, lportTag, serviceMode);
197         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
198
199         String flowName = "Ingress_DHCP_Server_v4" + dpId + "_" + lportTag + "_" + dhcpMacAddress + "_Permit_";
200         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
201                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
202     }
203
204     /**
205      * Add rule to ensure only DHCPv6 server traffic from the specified mac is
206      * allowed.
207      *
208      * @param dpId the dpid
209      * @param dhcpMacAddress the DHCP server mac address
210      * @param lportTag the lport tag
211      * @param addOrRemove is write or delete
212      * @param protoPortMatchPriority the priority
213      */
214     protected void ingressAclDhcpv6AllowServerTraffic(BigInteger dpId, String dhcpMacAddress, int lportTag,
215             int addOrRemove, Integer protoPortMatchPriority) {
216         final List<MatchInfoBase> matches = AclServiceUtils.buildDhcpV6Matches(AclConstants.DHCP_SERVER_PORT_IPV6,
217                 AclConstants.DHCP_CLIENT_PORT_IPV6, lportTag, serviceMode);
218         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
219
220         String flowName =
221                 "Ingress_DHCP_Server_v6" + "_" + dpId + "_" + lportTag + "_" + "_" + dhcpMacAddress + "_Permit_";
222         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_DHCP_SERVER_MATCH_PRIORITY, "ACL", 0, 0,
223                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
224     }
225
226     /**
227      * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), NS (135), NA (136) are allowed into the VM.
228      *
229      * @param dpId the dpid
230      * @param lportTag the lport tag
231      * @param addOrRemove is write or delete
232      */
233     private void ingressAclIcmpv6AllowedTraffic(BigInteger dpId, int lportTag, int addOrRemove) {
234         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
235
236         // Allow ICMPv6 Multicast Listener Query packets.
237         List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
238                 lportTag, serviceMode);
239
240         final short tableId = getAclAntiSpoofingTable();
241         String flowName =
242                 "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_MLD_QUERY + "_Permit_";
243         syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
244                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
245
246         // Allow ICMPv6 Neighbor Solicitation packets.
247         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
248
249         flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NS + "_Permit_";
250         syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
251                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
252
253         // Allow ICMPv6 Neighbor Advertisement packets.
254         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
255
256         flowName = "Ingress_ICMPv6" + "_" + dpId + "_" + lportTag + "_" + AclConstants.ICMPV6_TYPE_NA + "_Permit_";
257         syncFlow(dpId, tableId, flowName, AclConstants.PROTO_IPV6_ALLOWED_PRIORITY, "ACL", 0, 0,
258                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
259     }
260
261     /**
262      * Adds the rule to allow arp packets.
263      *
264      * @param dpId the dpId
265      * @param lportTag the lport tag
266      * @param addOrRemove whether to add or remove the flow
267      */
268     protected void programArpRule(BigInteger dpId, int lportTag, int addOrRemove) {
269         List<MatchInfoBase> matches = new ArrayList<>();
270         matches.add(MatchEthernetType.ARP);
271         matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
272         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
273         LOG.debug(addOrRemove == NwConstants.DEL_FLOW ? "Deleting " : "Adding " + "ARP Rule on DPID {}, "
274                 + "lportTag {}", dpId, lportTag);
275         String flowName = "Ingress_ARP_" + dpId + "_" + lportTag;
276         syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_ARP_TRAFFIC_MATCH_PRIORITY, "ACL", 0, 0,
277                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
278     }
279
280
281     /**
282      * Programs broadcast rules.
283      *
284      * @param port the Acl Interface port
285      * @param addOrRemove whether to delete or add flow
286      */
287     @Override
288     protected void programBroadcastRules(AclInterface port, int addOrRemove) {
289         programIpv4BroadcastRule(port, addOrRemove);
290     }
291
292     /**
293      * Programs IPv4 broadcast rules.
294      *
295      * @param port the Acl Interface port
296      * @param addOrRemove whether to delete or add flow
297      */
298     private void programIpv4BroadcastRule(AclInterface port, int addOrRemove) {
299         BigInteger dpId = port.getDpId();
300         int lportTag = port.getLPortTag();
301         MatchInfoBase lportMatchInfo = AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode);
302         List<IpPrefixOrAddress> cidrs = port.getSubnetIpPrefixes();
303         if (cidrs != null) {
304             List<String> broadcastAddresses = AclServiceUtils.getIpBroadcastAddresses(cidrs);
305             for (String broadcastAddress : broadcastAddresses) {
306                 List<MatchInfoBase> matches =
307                         AclServiceUtils.buildBroadcastIpV4Matches(broadcastAddress);
308                 matches.add(lportMatchInfo);
309                 List<InstructionInfo> instructions = new ArrayList<>();
310                 instructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
311                 String flowName = "Ingress_v4_Broadcast_" + dpId + "_" + lportTag + "_" + broadcastAddress + "_Permit";
312                 syncFlow(dpId, getAclAntiSpoofingTable(), flowName, AclConstants.PROTO_MATCH_PRIORITY, "ACL", 0, 0,
313                         AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
314             }
315         } else {
316             LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
317         }
318     }
319
320     @Override
321     protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
322         return direction.equals(DirectionIngress.class);
323     }
324
325     @Override
326     protected short getAclAntiSpoofingTable() {
327         return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
328     }
329
330     @Override
331     protected short getAclConntrackClassifierTable() {
332         return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
333     }
334
335     @Override
336     protected short getAclConntrackSenderTable() {
337         return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
338     }
339
340     @Override
341     protected short getAclForExistingTrafficTable() {
342         return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
343     }
344
345     @Override
346     protected short getAclFilterCumDispatcherTable() {
347         return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
348     }
349
350     @Override
351     protected short getAclRuleBasedFilterTable() {
352         return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
353     }
354
355     @Override
356     protected short getAclRemoteAclTable() {
357         return NwConstants.EGRESS_REMOTE_ACL_TABLE;
358     }
359
360     @Override
361     protected short getAclCommitterTable() {
362         return NwConstants.EGRESS_ACL_COMMITTER_TABLE;
363     }
364 }