NETVIRT-1193: ACL dropping IPv6 RA packets from external router.
[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;
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;
49
50 /**
51  * Provides the implementation for ingress (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 IngressAclServiceImpl extends AbstractAclServiceImpl {
58
59     private static final Logger LOG = LoggerFactory.getLogger(IngressAclServiceImpl.class);
60
61     /**
62      * Initialize the member variables.
63      *
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
70      */
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,
75                 aclInterfaceCache);
76     }
77
78     /**
79      * Bind service.
80      *
81      * @param aclInterface the acl interface
82      */
83     @Override
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<>();
89             instructions.add(
90                     MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.EGRESS_ACL_DUMMY_TABLE, ++instructionKey));
91             int flowPriority = NwConstants.EGRESS_ACL_SERVICE_INDEX;
92             short serviceIndex =
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),
99                     serviceMode);
100
101             return Collections.singletonList(
102                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.put(LogicalDatastoreType.CONFIGURATION,
103                             path, serviceInfo, WriteTransaction.CREATE_MISSING_PARENTS)));
104         });
105     }
106
107     /**
108      * Unbind service.
109      *
110      * @param aclInterface the acl interface
111      */
112     @Override
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),
117                 serviceMode);
118
119         LOG.debug("UnBinding ACL service for interface {}", interfaceName);
120         jobCoordinator.enqueueJob(interfaceName, () -> Collections.singletonList(txRunner
121                 .callWithNewWriteOnlyTransactionAndSubmit(tx -> tx.delete(LogicalDatastoreType.CONFIGURATION, path))));
122     }
123
124     @Override
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);
129
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);
137
138             programArpRule(dpid, lportTag, addOrRemove);
139             programIpv4BroadcastRule(port, addOrRemove);
140         }
141     }
142
143     @Override
144     protected void programGotoClassifierTableRules(BigInteger dpId, List<AllowedAddressPairs> aaps, int lportTag,
145             int addOrRemove) {
146         for (AllowedAddressPairs aap : aaps) {
147             IpPrefixOrAddress attachIp = aap.getIpAddress();
148             MacAddress mac = aap.getMacAddress();
149
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));
154
155             List<InstructionInfo> gotoInstructions = new ArrayList<>();
156             gotoInstructions.add(new InstructionGotoTable(getAclConntrackClassifierTable()));
157
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);
162         }
163     }
164
165     @Override
166     protected void programRemoteAclTableFlow(BigInteger dpId, Integer aclTag, AllowedAddressPairs aap,
167             int addOrRemove) {
168         List<MatchInfoBase> flowMatches = new ArrayList<>();
169         flowMatches.addAll(AclServiceUtils.buildIpAndSrcServiceMatch(aclTag, aap));
170
171         List<InstructionInfo> instructions = AclServiceOFFlowBuilder.getGotoInstructionInfo(getAclCommitterTable());
172         String flowNameAdded = "Acl_Filter_Ingress_" + String.valueOf(aap.getIpAddress().getValue()) + "_" + aclTag;
173
174         syncFlow(dpId, getAclRemoteAclTable(), flowNameAdded, AclConstants.ACL_DEFAULT_PRIORITY, "ACL", 0, 0,
175                 AclConstants.COOKIE_ACL_BASE, flowMatches, instructions, addOrRemove);
176     }
177
178     /**
179      * Add rule to ensure only DHCP server traffic from the specified mac is
180      * allowed.
181      *
182      * @param dpId the dpid
183      * @param lportTag the lport tag
184      * @param addOrRemove is write or delete
185      */
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();
190
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);
194     }
195
196     /**
197      * Add rule to ensure only DHCPv6 server traffic from the specified mac is
198      * allowed.
199      *
200      * @param dpId the dpid
201      * @param lportTag the lport tag
202      * @param addOrRemove is write or delete
203      */
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();
208
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);
212     }
213
214     /**
215      * Add rules to ensure that certain ICMPv6 like MLD_QUERY (130), RS (134), NS (135), NA (136) are
216      * allowed into the VM.
217      *
218      * @param port the port
219      * @param addOrRemove is write or delete
220      */
221     private void ingressAclIcmpv6AllowedTraffic(AclInterface port, int addOrRemove) {
222         BigInteger dpId = port.getDpId();
223         int lportTag = port.getLPortTag();
224         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions();
225
226         // Allow ICMPv6 Multicast Listener Query packets.
227         List<MatchInfoBase> matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_MLD_QUERY, 0,
228                 lportTag, serviceMode);
229
230         final short tableId = getAclAntiSpoofingTable();
231         String flowName =
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);
235
236         // Allow ICMPv6 Neighbor Solicitation packets.
237         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NS, 0, lportTag, serviceMode);
238
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);
242
243         // Allow ICMPv6 Neighbor Advertisement packets.
244         matches = AclServiceUtils.buildIcmpV6Matches(AclConstants.ICMPV6_TYPE_NA, 0, lportTag, serviceMode);
245
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);
249     }
250
251     @Override
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)) {
256             return;
257         }
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));
265
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);
270     }
271
272     /**
273      * Adds the rule to allow arp packets.
274      *
275      * @param dpId the dpId
276      * @param lportTag the lport tag
277      * @param addOrRemove whether to add or remove the flow
278      */
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",
285                 dpId, lportTag);
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);
289     }
290
291
292     /**
293      * Programs broadcast rules.
294      *
295      * @param port the Acl Interface port
296      * @param addOrRemove whether to delete or add flow
297      */
298     @Override
299     protected void programBroadcastRules(AclInterface port, int addOrRemove) {
300         programIpv4BroadcastRule(port, addOrRemove);
301     }
302
303     /**
304      * Programs IPv4 broadcast rules.
305      *
306      * @param port the Acl Interface port
307      * @param addOrRemove whether to delete or add flow
308      */
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);
325             }
326         } else {
327             LOG.warn("IP Broadcast CIDRs are missing for port {}", port.getInterfaceId());
328         }
329     }
330
331     @Override
332     protected boolean isValidDirection(Class<? extends DirectionBase> direction) {
333         return direction.equals(DirectionIngress.class);
334     }
335
336     @Override
337     protected short getAclAntiSpoofingTable() {
338         return NwConstants.EGRESS_ACL_ANTI_SPOOFING_TABLE;
339     }
340
341     @Override
342     protected short getAclConntrackClassifierTable() {
343         return NwConstants.EGRESS_ACL_CONNTRACK_CLASSIFIER_TABLE;
344     }
345
346     @Override
347     protected short getAclConntrackSenderTable() {
348         return NwConstants.EGRESS_ACL_CONNTRACK_SENDER_TABLE;
349     }
350
351     @Override
352     protected short getAclForExistingTrafficTable() {
353         return NwConstants.EGRESS_ACL_FOR_EXISTING_TRAFFIC_TABLE;
354     }
355
356     @Override
357     protected short getAclFilterCumDispatcherTable() {
358         return NwConstants.EGRESS_ACL_FILTER_CUM_DISPATCHER_TABLE;
359     }
360
361     @Override
362     protected short getAclRuleBasedFilterTable() {
363         return NwConstants.EGRESS_ACL_RULE_BASED_FILTER_TABLE;
364     }
365
366     @Override
367     protected short getAclRemoteAclTable() {
368         return NwConstants.EGRESS_REMOTE_ACL_TABLE;
369     }
370
371     @Override
372     protected short getAclCommitterTable() {
373         return NwConstants.EGRESS_ACL_COMMITTER_TABLE;
374     }
375 }