2 * Copyright © 2016, 2017 Red Hat, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.aclservice;
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;
17 import com.google.common.base.Optional;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
21 import java.math.BigInteger;
23 import java.util.Collections;
24 import java.util.concurrent.Future;
26 import javax.inject.Inject;
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;
89 @RunWith(MockitoJUnitRunner.class)
91 public class LearnEgressAclServiceImplTest {
93 public @Rule MethodRule guice = new GuiceRule(new AclServiceModule(),
94 new AclServiceTestModule(SecurityGroupMode.Learn), new TestableDataTreeChangeListenerModule());
96 private static final Long ELAN_TAG = 500L;
98 private static final String SG_UUID = "12345678-1234-1234-1234-123456789012";
100 private static final long ACL_ID = 1L;
102 LearnEgressAclServiceImpl testedService;
105 DataBroker dataBroker;
107 IMdsalApiManager mdsalManager;
109 WriteTransaction mockWriteTx;
111 ReadOnlyTransaction mockReadTx;
113 AclserviceConfig config;
115 IdManagerService idManager;
118 AsyncEventsWaiter asyncEventsWaiter;
120 MethodInvocationParamSaver<Future<?>> installFlowValueSaver = null;
121 MethodInvocationParamSaver<Future<?>> removeFlowValueSaver = null;
123 final Integer tcpFinIdleTimeoutValue = 60;
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();
146 public void addAcl__NullInterface() {
147 assertEquals(false, testedService.applyAcl(null));
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));
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());
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);
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()));
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());
187 FlowEntity flow = (FlowEntity) installFlowValueSaver.getInvocationParams(9).get(1);
188 AclServiceTestUtils.verifyActionTypeExist(flow.getInstructionInfoList().get(0), ActionLearn.class);
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)));
202 FlowEntity secondRangeFlow = (FlowEntity) installFlowValueSaver.getInvocationParams(10).get(1);
203 assertTrue(secondRangeFlow.getMatchInfoList().contains(new NxMatchTcpDestinationPort(84, 65535)));
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);
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()));
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)));
238 private AclInterface stubUdpAclInterface(Uuid sgUuid, String ifName, String ipv4PrefixStr, int tcpPortLower,
240 AclInterface ai = new AclInterface();
241 ai.setPortSecurityEnabled(true);
242 ai.setSecurityGroups(Collections.singletonList(sgUuid));
243 ai.setDpId(BigInteger.ONE);
245 stubInterfaceAcl(ifName, ai);
247 stubAccessList(sgUuid, ipv4PrefixStr, tcpPortLower, tcpPortUpper, (short) NwConstants.IP_PROT_UDP);
251 private AclInterface stubTcpAclInterface(Uuid sgUuid, String ifName, String ipv4PrefixStr, int tcpPortLower,
253 AclInterface ai = new AclInterface();
254 ai.setPortSecurityEnabled(true);
255 ai.setDpId(BigInteger.ONE);
257 ai.setSecurityGroups(Collections.singletonList(sgUuid));
258 stubInterfaceAcl(ifName, ai);
260 stubAccessList(sgUuid, ipv4PrefixStr, tcpPortLower, tcpPortUpper, (short) NwConstants.IP_PROT_TCP);
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()));
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);
277 stubInterfaceAcl(ifName, ai);
279 stubAccessList(sgUuid, null, -1, -1, (short) -1);
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));
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());
295 if (ipv4PrefixStr != null) {
296 AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
297 Ipv4Prefix ipv4Prefix = new Ipv4Prefix(ipv4PrefixStr);
298 aceIpv4Builder.setSourceIpv4Network(ipv4Prefix);
299 aceIpBuilder.setAceIpVersion(aceIpv4Builder.build());
301 if (protocol != -1) {
302 aceIpBuilder.setProtocol(protocol);
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());
317 InstanceIdentifier<Acl> aclKey = AclServiceUtils.getAclInstanceIdentifier(sgUuid.getValue());
318 when(mockReadTx.read(LogicalDatastoreType.CONFIGURATION, aclKey))
319 .thenReturn(Futures.immediateCheckedFuture(Optional.of(ab.build())));
322 private ListenableFuture<RpcResult<AllocateIdOutput>> getAclIdResult(Long id) {
323 AllocateIdOutputBuilder output = new AllocateIdOutputBuilder();
324 output.setIdValue(id);
326 RpcResultBuilder<AllocateIdOutput> allocateIdRpcBuilder = RpcResultBuilder.success();
327 allocateIdRpcBuilder.withResult(output.build());
328 ListenableFuture<RpcResult<AllocateIdOutput>> idResult = Futures.immediateFuture(allocateIdRpcBuilder.build());