Bug 7730: Traffic drop in ACL during port update for AAP
[netvirt.git] / vpnservice / aclservice / impl / src / test / java / org / opendaylight / netvirt / aclservice / LearnEgressAclServiceImplTest.java
1 /*
2  * Copyright © 2016, 2017 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.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Mockito.doAnswer;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.when;
16
17 import com.google.common.base.Optional;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20
21 import java.math.BigInteger;
22
23 import java.util.Collections;
24 import java.util.concurrent.Future;
25
26 import javax.inject.Inject;
27
28 import org.junit.Before;
29 import org.junit.Ignore;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.rules.MethodRule;
33 import org.junit.runner.RunWith;
34 import org.mockito.Mock;
35 import org.mockito.runners.MockitoJUnitRunner;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
38 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
40 import org.opendaylight.genius.datastoreutils.testutils.AsyncEventsWaiter;
41 import org.opendaylight.genius.datastoreutils.testutils.TestableDataTreeChangeListenerModule;
42 import org.opendaylight.genius.mdsalutil.FlowEntity;
43 import org.opendaylight.genius.mdsalutil.NwConstants;
44 import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
45 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
46 import org.opendaylight.genius.mdsalutil.matches.MatchTcpFlags;
47 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchTcpDestinationPort;
48 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchUdpDestinationPort;
49 import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule;
50 import org.opendaylight.netvirt.aclservice.api.utils.AclInterface;
51 import org.opendaylight.netvirt.aclservice.tests.AclServiceModule;
52 import org.opendaylight.netvirt.aclservice.tests.AclServiceTestModule;
53 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
54 import org.opendaylight.netvirt.aclservice.utils.AclDataUtil;
55 import org.opendaylight.netvirt.aclservice.utils.AclServiceTestUtils;
56 import org.opendaylight.netvirt.aclservice.utils.AclServiceUtils;
57 import org.opendaylight.netvirt.aclservice.utils.MethodInvocationParamSaver;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.Ipv4Acl;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclBuilder;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntriesBuilder;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceBuilder;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.ActionsBuilder;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.MatchesBuilder;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRangeBuilder;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutputBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.config.rev160806.AclserviceConfig;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.config.rev160806.AclserviceConfig.SecurityGroupMode;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionEgress;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.IpPrefixOrAddress;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttrBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairsBuilder;
85 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
86 import org.opendaylight.yangtools.yang.common.RpcResult;
87 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
88
89 @RunWith(MockitoJUnitRunner.class)
90 @Ignore
91 public class LearnEgressAclServiceImplTest {
92
93     public @Rule MethodRule guice = new GuiceRule(new AclServiceModule(),
94             new AclServiceTestModule(SecurityGroupMode.Learn), new TestableDataTreeChangeListenerModule());
95
96     private static final Long ELAN_TAG = 500L;
97
98     private static final String SG_UUID = "12345678-1234-1234-1234-123456789012";
99
100     private static final long ACL_ID = 1L;
101
102     LearnEgressAclServiceImpl testedService;
103
104     @Mock
105     DataBroker dataBroker;
106     @Mock
107     IMdsalApiManager mdsalManager;
108     @Mock
109     WriteTransaction mockWriteTx;
110     @Mock
111     ReadOnlyTransaction mockReadTx;
112     @Mock
113     AclserviceConfig config;
114     @Mock
115     IdManagerService idManager;
116
117     @Inject
118     AsyncEventsWaiter asyncEventsWaiter;
119
120     MethodInvocationParamSaver<Future<?>> installFlowValueSaver = null;
121     MethodInvocationParamSaver<Future<?>> removeFlowValueSaver = null;
122
123     final Integer tcpFinIdleTimeoutValue = 60;
124
125     @Before
126     public void setUp() {
127         AclDataUtil aclDataUtil = new AclDataUtil();
128         ListenableFuture<RpcResult<AllocateIdOutput>> idResult = getAclIdResult(ACL_ID);
129         doReturn(idResult).when(idManager).allocateId(any(AllocateIdInput.class));
130         AclServiceUtils aclServiceUtils = new AclServiceUtils(aclDataUtil, config, idManager);
131         testedService = new LearnEgressAclServiceImpl(dataBroker, mdsalManager, aclDataUtil, aclServiceUtils);
132         RpcResult<Object> allocateIdResult = RpcResultBuilder.success().withResult(new AllocateIdOutputBuilder()
133                 .setIdValue(10L).build()).build();
134         doReturn(Futures.immediateFuture(allocateIdResult)).when(idManager).allocateId(any(AllocateIdInput.class));
135         doReturn(Futures.immediateCheckedFuture(null)).when(mockWriteTx).submit();
136         doReturn(mockReadTx).when(dataBroker).newReadOnlyTransaction();
137         doReturn(mockWriteTx).when(dataBroker).newWriteOnlyTransaction();
138         installFlowValueSaver = new MethodInvocationParamSaver<>(Futures.immediateCheckedFuture(null));
139         doAnswer(installFlowValueSaver).when(mdsalManager).installFlow(any(BigInteger.class), any(FlowEntity.class));
140         removeFlowValueSaver = new MethodInvocationParamSaver<>(Futures.immediateCheckedFuture(null));
141         doAnswer(removeFlowValueSaver).when(mdsalManager).removeFlow(any(BigInteger.class), any(FlowEntity.class));
142         doReturn(tcpFinIdleTimeoutValue).when(config).getSecurityGroupTcpFinIdleTimeout();
143     }
144
145     @Test
146     public void addAcl__NullInterface() {
147         assertEquals(false, testedService.applyAcl(null));
148     }
149
150     @Test
151     public void addAcl__MissingInterfaceStateShouldFail() throws Exception {
152         AclInterface ai = new AclInterface();
153         ai.setPortSecurityEnabled(true);
154         ai.setDpId(BigInteger.ONE);
155         assertEquals(false, testedService.applyAcl(ai));
156     }
157
158     @Test
159     public void addAcl__SinglePort() throws Exception {
160         AclServiceTestUtils.prepareElanTag(mockReadTx, ELAN_TAG);
161         Uuid sgUuid = new Uuid(SG_UUID);
162         AclInterface ai = stubTcpAclInterface(sgUuid, "if_name", "1.1.1.1/32", 80, 80);
163         assertEquals(true, testedService.applyAcl(ai));
164         AclServiceTestUtils.waitABit(asyncEventsWaiter);
165         assertEquals(10, installFlowValueSaver.getNumOfInvocations());
166
167         FlowEntity flow = (FlowEntity) installFlowValueSaver.getInvocationParams(9).get(1);
168         assertTrue(flow.getMatchInfoList().contains(new NxMatchTcpDestinationPort(80, 65535)));
169         AclServiceTestUtils.verifyActionTypeExist(flow.getInstructionInfoList().get(0), ActionLearn.class);
170
171         // verify that tcpFinIdleTimeout is used for TCP
172         AclServiceTestUtils.verifyActionLearn(flow.getInstructionInfoList().get(0),
173                 new ActionLearn(0, 0, AclConstants.PROTO_MATCH_PRIORITY, AclConstants.COOKIE_ACL_BASE,
174                         AclConstants.LEARN_DELETE_LEARNED_FLAG_VALUE, NwConstants.EGRESS_LEARN_TABLE,
175                         tcpFinIdleTimeoutValue, 0, Collections.emptyList()));
176     }
177
178     @Test
179     public void addAcl__AllowAll() throws Exception {
180         AclServiceTestUtils.prepareElanTag(mockReadTx, ELAN_TAG);
181         Uuid sgUuid = new Uuid(SG_UUID);
182         AclInterface ai = stubAllowAllInterface(sgUuid, "if_name");
183         assertEquals(true, testedService.applyAcl(ai));
184         AclServiceTestUtils.waitABit(asyncEventsWaiter);
185         assertEquals(10, installFlowValueSaver.getNumOfInvocations());
186
187         FlowEntity flow = (FlowEntity) installFlowValueSaver.getInvocationParams(9).get(1);
188         AclServiceTestUtils.verifyActionTypeExist(flow.getInstructionInfoList().get(0), ActionLearn.class);
189     }
190
191     @Test
192     public void addAcl__MultipleRanges() throws Exception {
193         AclServiceTestUtils.prepareElanTag(mockReadTx, ELAN_TAG);
194         Uuid sgUuid = new Uuid(SG_UUID);
195         AclInterface ai = stubTcpAclInterface(sgUuid, "if_name", "1.1.1.1/32", 80, 84);
196         assertEquals(true, testedService.applyAcl(ai));
197         AclServiceTestUtils.waitABit(asyncEventsWaiter);
198         assertEquals(11, installFlowValueSaver.getNumOfInvocations());
199         FlowEntity firstRangeFlow = (FlowEntity) installFlowValueSaver.getInvocationParams(9).get(1);
200         assertTrue(firstRangeFlow.getMatchInfoList().contains(new NxMatchTcpDestinationPort(80, 65532)));
201
202         FlowEntity secondRangeFlow = (FlowEntity) installFlowValueSaver.getInvocationParams(10).get(1);
203         assertTrue(secondRangeFlow.getMatchInfoList().contains(new NxMatchTcpDestinationPort(84, 65535)));
204     }
205
206     @Test
207     public void addAcl__UdpSinglePortShouldNotCreateSynRule() throws Exception {
208         AclServiceTestUtils.prepareElanTag(mockReadTx, ELAN_TAG);
209         Uuid sgUuid = new Uuid(SG_UUID);
210         AclInterface ai = stubUdpAclInterface(sgUuid, "if_name", "1.1.1.1/32", 80, 80);
211         assertEquals(true, testedService.applyAcl(ai));
212         AclServiceTestUtils.waitABit(asyncEventsWaiter);
213         assertEquals(10, installFlowValueSaver.getNumOfInvocations());
214         FlowEntity flow = (FlowEntity) installFlowValueSaver.getInvocationParams(9).get(1);
215         assertTrue(flow.getMatchInfoList().contains(new NxMatchUdpDestinationPort(80, 65535)));
216         AclServiceTestUtils.verifyActionTypeExist(flow.getInstructionInfoList().get(0), ActionLearn.class);
217
218         // verify that even though tcpFinIdleTimeout is set to non-zero, it is not used for UDP
219         AclServiceTestUtils.verifyActionLearn(flow.getInstructionInfoList().get(0),
220                 new ActionLearn(0, 0, AclConstants.PROTO_MATCH_PRIORITY, AclConstants.COOKIE_ACL_BASE,
221                         AclConstants.LEARN_DELETE_LEARNED_FLAG_VALUE, NwConstants.EGRESS_LEARN_TABLE, 0, 0,
222                         Collections.emptyList()));
223     }
224
225     @Test
226     @Ignore
227     public void removeAcl__SinglePort() throws Exception {
228         Uuid sgUuid = new Uuid(SG_UUID);
229         AclInterface ai = stubTcpAclInterface(sgUuid, "if_name", "1.1.1.1/32", 80, 80);
230         assertEquals(true, testedService.removeAcl(ai));
231         AclServiceTestUtils.waitABit(asyncEventsWaiter);
232         assertEquals(5, removeFlowValueSaver.getNumOfInvocations());
233         FlowEntity firstRangeFlow = (FlowEntity) removeFlowValueSaver.getInvocationParams(4).get(1);
234         assertTrue(firstRangeFlow.getMatchInfoList().contains(new MatchTcpFlags(2)));
235         assertTrue(firstRangeFlow.getMatchInfoList().contains(new NxMatchTcpDestinationPort(80, 65535)));
236     }
237
238     private AclInterface stubUdpAclInterface(Uuid sgUuid, String ifName, String ipv4PrefixStr, int tcpPortLower,
239             int tcpPortUpper) {
240         AclInterface ai = new AclInterface();
241         ai.setPortSecurityEnabled(true);
242         ai.setSecurityGroups(Collections.singletonList(sgUuid));
243         ai.setDpId(BigInteger.ONE);
244         ai.setLPortTag(2);
245         stubInterfaceAcl(ifName, ai);
246
247         stubAccessList(sgUuid, ipv4PrefixStr, tcpPortLower, tcpPortUpper, (short) NwConstants.IP_PROT_UDP);
248         return ai;
249     }
250
251     private AclInterface stubTcpAclInterface(Uuid sgUuid, String ifName, String ipv4PrefixStr, int tcpPortLower,
252             int tcpPortUpper) {
253         AclInterface ai = new AclInterface();
254         ai.setPortSecurityEnabled(true);
255         ai.setDpId(BigInteger.ONE);
256         ai.setLPortTag(2);
257         ai.setSecurityGroups(Collections.singletonList(sgUuid));
258         stubInterfaceAcl(ifName, ai);
259
260         stubAccessList(sgUuid, ipv4PrefixStr, tcpPortLower, tcpPortUpper, (short) NwConstants.IP_PROT_TCP);
261         return ai;
262     }
263
264     private void stubInterfaceAcl(String ifName, AclInterface ai) {
265         AllowedAddressPairsBuilder aapb = new AllowedAddressPairsBuilder();
266         aapb.setIpAddress(new IpPrefixOrAddress("1.1.1.1/32".toCharArray()));
267         aapb.setMacAddress(new MacAddress("AA:BB:CC:DD:EE:FF"));
268         ai.setAllowedAddressPairs(Collections.singletonList(aapb.build()));
269     }
270
271     private AclInterface stubAllowAllInterface(Uuid sgUuid, String ifName) {
272         AclInterface ai = new AclInterface();
273         ai.setPortSecurityEnabled(true);
274         ai.setSecurityGroups(Collections.singletonList(sgUuid));
275         ai.setDpId(BigInteger.ONE);
276         ai.setLPortTag(2);
277         stubInterfaceAcl(ifName, ai);
278
279         stubAccessList(sgUuid, null, -1, -1, (short) -1);
280         return ai;
281     }
282
283     private void stubAccessList(Uuid sgUuid, String ipv4PrefixStr, int portLower, int portUpper, short protocol) {
284         AclBuilder ab = new AclBuilder();
285         ab.setAclName("AAA");
286         ab.setKey(new AclKey(sgUuid.getValue(), Ipv4Acl.class));
287
288         AceIpBuilder aceIpBuilder = new AceIpBuilder();
289         if (portLower != -1 && portUpper != -1) {
290             DestinationPortRangeBuilder dprb = new DestinationPortRangeBuilder();
291             dprb.setLowerPort(new PortNumber(portLower));
292             dprb.setUpperPort(new PortNumber(portUpper));
293             aceIpBuilder.setDestinationPortRange(dprb.build());
294         }
295         if (ipv4PrefixStr != null) {
296             AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
297             Ipv4Prefix ipv4Prefix = new Ipv4Prefix(ipv4PrefixStr);
298             aceIpv4Builder.setSourceIpv4Network(ipv4Prefix);
299             aceIpBuilder.setAceIpVersion(aceIpv4Builder.build());
300         }
301         if (protocol != -1) {
302             aceIpBuilder.setProtocol(protocol);
303         }
304         MatchesBuilder matches = new MatchesBuilder();
305         matches.setAceType(aceIpBuilder.build());
306         AceBuilder aceBuilder = new AceBuilder();
307         ActionsBuilder actions = new ActionsBuilder().setPacketHandling(new PermitBuilder().build());
308         aceBuilder.setActions(actions.build());
309         aceBuilder.setMatches(matches.build());
310         SecurityRuleAttrBuilder securityRuleAttrBuilder = new SecurityRuleAttrBuilder();
311         securityRuleAttrBuilder.setDirection(DirectionEgress.class);
312         aceBuilder.addAugmentation(SecurityRuleAttr.class, securityRuleAttrBuilder.build());
313         AccessListEntriesBuilder aleb = new AccessListEntriesBuilder();
314         aleb.setAce(Collections.singletonList(aceBuilder.build()));
315         ab.setAccessListEntries(aleb.build());
316
317         InstanceIdentifier<Acl> aclKey = AclServiceUtils.getAclInstanceIdentifier(sgUuid.getValue());
318         when(mockReadTx.read(LogicalDatastoreType.CONFIGURATION, aclKey))
319                 .thenReturn(Futures.immediateCheckedFuture(Optional.of(ab.build())));
320     }
321
322     private ListenableFuture<RpcResult<AllocateIdOutput>> getAclIdResult(Long id) {
323         AllocateIdOutputBuilder output = new AllocateIdOutputBuilder();
324         output.setIdValue(id);
325
326         RpcResultBuilder<AllocateIdOutput> allocateIdRpcBuilder = RpcResultBuilder.success();
327         allocateIdRpcBuilder.withResult(output.build());
328         ListenableFuture<RpcResult<AllocateIdOutput>> idResult = Futures.immediateFuture(allocateIdRpcBuilder.build());
329         return idResult;
330     }
331
332 }