summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Gradzki <mgradzki@cisco.com>2016-08-25 16:58:55 +0200
committerMarek Gradzki <mgradzki@cisco.com>2016-08-26 12:39:27 +0200
commita92df9b4a2c5488621f8e95a6b88fb126a375649 (patch)
tree227321c603471314930e64f25a255589f5862d18
parentb257036c963d274e2bcee43adc309782e5ddeab5 (diff)
HONEYCOMB-139: ietf-acl translation layer. IP4 L3 ACL support
Change-Id: I5e5af0d7609aa594790b35a387ec8701f1f6b6df Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java203
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java1
-rw-r--r--v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java106
-rw-r--r--vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/v3po/util/TranslateUtils.java6
4 files changed, 315 insertions, 1 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java
new file mode 100644
index 000000000..ac110f98b
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.translate.v3po.interfaces.acl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static io.fd.honeycomb.translate.v3po.util.TranslateUtils.ipv4AddressNoZoneToArray;
+
+import com.google.common.primitives.Ints;
+import javax.annotation.Nonnull;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.openvpp.jvpp.core.dto.ClassifyAddDelSession;
+import org.openvpp.jvpp.core.dto.ClassifyAddDelTable;
+import org.openvpp.jvpp.core.dto.InputAclSetInterface;
+import org.openvpp.jvpp.core.future.FutureJVppCore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class AceIp4Writer extends AbstractAceWriter<AceIp> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class);
+ private static final int TABLE_MASK_LENGTH = 48; // number of bytes
+ static final int MATCH_N_VECTORS = 3; // number of 16B vectors
+ static final int TABLE_MEM_SIZE = 8 * 1024;
+ private static final int IP_MASK_LENGTH = 32; // number of bits
+ private static final int IP_VERSION_OFFSET = 14;
+ private static final int DSCP_OFFSET = 15;
+ private static final int SRC_IP_OFFSET = 26;
+ private static final int DST_IP_OFFSET = 30;
+ private static final int IP_VERSION_MASK = 0xf0;
+ private static final int DSCP_MASK = 0xfc;
+
+ public AceIp4Writer(@Nonnull final FutureJVppCore futureJVppCore) {
+ super(futureJVppCore);
+ }
+
+ @Override
+ public ClassifyAddDelTable getClassifyAddDelTableRequest(@Nonnull final PacketHandling action,
+ @Nonnull final AceIp aceIp,
+ @Nonnull final int nextTableIndex) {
+ checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp);
+ final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion();
+
+ final ClassifyAddDelTable request = new ClassifyAddDelTable();
+ request.isAdd = 1;
+ request.tableIndex = -1; // value not present
+
+ request.nbuckets = 1; // we expect exactly one session per table
+
+ if (action instanceof Permit) {
+ request.missNextIndex = 0; // for list of permit rules, deny (0) should be default action
+ } else { // deny is default value
+ request.missNextIndex = -1; // for list of deny rules, permit (-1) should be default action
+ }
+
+ request.nextTableIndex = nextTableIndex;
+ request.skipNVectors = 0; // match entire L2 and L3 header
+ request.matchNVectors = MATCH_N_VECTORS;
+ request.memorySize = TABLE_MEM_SIZE;
+
+ boolean aceIsEmpty = true;
+ request.mask = new byte[TABLE_MASK_LENGTH];
+
+ // First 14 bytes represent l2 header (2x6 + etherType(2)_
+ if (aceIp.getProtocol() != null) { // Internet Protocol number
+ request.mask[IP_VERSION_OFFSET] = (byte) IP_VERSION_MASK; // first 4 bits
+ }
+
+ if (aceIp.getDscp() != null) {
+ aceIsEmpty = false;
+ request.mask[DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits
+ }
+
+ if (aceIp.getSourcePortRange() != null) {
+ LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
+ }
+
+ if (aceIp.getDestinationPortRange() != null) {
+ LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
+ }
+
+ if (ipVersion.getSourceIpv4Network() != null) {
+ aceIsEmpty = false;
+ System.arraycopy(toByteMask(ipVersion.getSourceIpv4Network()), 0, request.mask, SRC_IP_OFFSET, 4);
+ }
+
+ if (ipVersion.getDestinationIpv4Network() != null) {
+ aceIsEmpty = false;
+ System.arraycopy(toByteMask(ipVersion.getDestinationIpv4Network()), 0, request.mask, DST_IP_OFFSET, 4);
+ }
+
+ if (aceIsEmpty) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define packet field matches", aceIp.toString()));
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceIp,
+ ReflectionToStringBuilder.toString(request));
+ }
+ return request;
+ }
+
+ @Override
+ public ClassifyAddDelSession getClassifyAddDelSessionRequest(@Nonnull final PacketHandling action,
+ @Nonnull final AceIp aceIp,
+ @Nonnull final int tableIndex) {
+ checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp);
+ final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion();
+
+ final ClassifyAddDelSession request = new ClassifyAddDelSession();
+ request.isAdd = 1;
+ request.tableIndex = tableIndex;
+
+ if (action instanceof Permit) {
+ request.hitNextIndex = -1;
+ } // deny (0) is default value
+
+ request.match = new byte[TABLE_MASK_LENGTH];
+ boolean noMatch = true;
+
+ if (aceIp.getProtocol() != null) {
+ request.match[IP_VERSION_OFFSET] = (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4));
+ }
+
+ if (aceIp.getDscp() != null) {
+ noMatch = false;
+ request.match[DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2));
+ }
+
+ if (aceIp.getSourcePortRange() != null) {
+ LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
+ }
+
+ if (aceIp.getDestinationPortRange() != null) {
+ LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
+ }
+
+ if (ipVersion.getSourceIpv4Network() != null) {
+ noMatch = false;
+ System.arraycopy(toMatchValue(ipVersion.getSourceIpv4Network()), 0, request.match, SRC_IP_OFFSET, 4);
+ }
+
+ if (ipVersion.getDestinationIpv4Network() != null) {
+ noMatch = false;
+ System.arraycopy(toMatchValue(ipVersion.getDestinationIpv4Network()), 0, request.match, DST_IP_OFFSET, 4);
+ }
+
+ if (noMatch) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define neither source nor destination MAC address", aceIp.toString()));
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp,
+ ReflectionToStringBuilder.toString(request));
+ }
+ return request;
+ }
+
+ @Override
+ protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) {
+ request.ip4TableIndex = tableIndex;
+ }
+
+ private static byte[] toByteMask(final int prefixLength) {
+ final long mask = ((1L << prefixLength) - 1) << (IP_MASK_LENGTH - prefixLength);
+ return Ints.toByteArray((int) mask);
+ }
+
+ private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) {
+ final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]);
+ return toByteMask(prefixLength);
+ }
+
+ private static byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) {
+ final String[] split = ipv4Prefix.getValue().split("/");
+ final byte[] addressBytes = ipv4AddressNoZoneToArray(split[0]);
+ final byte[] mask = toByteMask(Byte.valueOf(split[1]));
+ for (int i = 0; i < addressBytes.length; ++i) {
+ addressBytes[i] &= mask[i];
+ }
+ return addressBytes;
+ }
+}
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java
index 5eb4af5e1..58741cfeb 100644
--- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java
+++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java
@@ -64,6 +64,7 @@ public final class IetfAClWriter {
public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) {
this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
aceWriters.put(AclType.ETH, new AceEthWriter(futureJVppCore));
+ aceWriters.put(AclType.IP4, new AceIp4Writer(futureJVppCore));
}
void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java
new file mode 100644
index 000000000..5be0ea069
--- /dev/null
+++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.translate.v3po.interfaces.acl;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Dscp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.openvpp.jvpp.core.dto.ClassifyAddDelSession;
+import org.openvpp.jvpp.core.dto.ClassifyAddDelTable;
+import org.openvpp.jvpp.core.dto.InputAclSetInterface;
+import org.openvpp.jvpp.core.future.FutureJVppCore;
+
+public class AceIp4WriterTest {
+ @Mock
+ private FutureJVppCore jvpp;
+ private AceIp4Writer writer;
+ private PacketHandling action;
+ private AceIp aceIp;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ writer = new AceIp4Writer(jvpp);
+ action = new DenyBuilder().setDeny(true).build();
+ aceIp = new AceIpBuilder()
+ .setProtocol((short) 4)
+ .setDscp(new Dscp((short) 11))
+ .setAceIpVersion(new AceIpv4Builder()
+ .setSourceIpv4Network(new Ipv4Prefix("1.2.3.4/32"))
+ .setDestinationIpv4Network(new Ipv4Prefix("1.2.4.5/24"))
+ .build())
+ .build();
+ }
+
+ @Test
+ public void testGetClassifyAddDelTableRequest() throws Exception {
+ final int nextTableIndex = 42;
+ final ClassifyAddDelTable request = writer.getClassifyAddDelTableRequest(action, aceIp, nextTableIndex);
+
+ assertEquals(1, request.isAdd);
+ assertEquals(-1, request.tableIndex);
+ assertEquals(1, request.nbuckets);
+ assertEquals(-1, request.missNextIndex);
+ assertEquals(nextTableIndex, request.nextTableIndex);
+ assertEquals(0, request.skipNVectors);
+ assertEquals(AceIp4Writer.MATCH_N_VECTORS, request.matchNVectors);
+ assertEquals(AceIp4Writer.TABLE_MEM_SIZE, request.memorySize);
+
+ byte[] expectedMask = new byte[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xf0, (byte) 0xfc,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1,
+ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assertArrayEquals(expectedMask, request.mask);
+ }
+
+ @Test
+ public void testGetClassifyAddDelSessionRequest() throws Exception {
+ final int tableIndex = 123;
+ final ClassifyAddDelSession request = writer.getClassifyAddDelSessionRequest(action, aceIp, tableIndex);
+
+ assertEquals(1, request.isAdd);
+ assertEquals(tableIndex, request.tableIndex);
+ assertEquals(0, request.hitNextIndex);
+
+ byte[] expectedMatch = new byte[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0x40, (byte) 0x2c,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 1, 2,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assertArrayEquals(expectedMatch, request.match);
+ }
+
+ @Test
+ public void testSetClassifyTable() throws Exception {
+ final int tableIndex = 321;
+ final InputAclSetInterface request = new InputAclSetInterface();
+ writer.setClassifyTable(request, tableIndex);
+ assertEquals(tableIndex, request.ip4TableIndex);
+ }
+} \ No newline at end of file
diff --git a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/v3po/util/TranslateUtils.java b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/v3po/util/TranslateUtils.java
index 4ecf8ff91..0902064eb 100644
--- a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/v3po/util/TranslateUtils.java
+++ b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/v3po/util/TranslateUtils.java
@@ -109,8 +109,12 @@ public final class TranslateUtils {
* @return byte array with address bytes
*/
public static byte[] ipv4AddressNoZoneToArray(final Ipv4AddressNoZone ipv4Addr) {
+ return ipv4AddressNoZoneToArray(ipv4Addr.getValue());
+ }
+
+ public static byte[] ipv4AddressNoZoneToArray(final String ipv4Addr) {
byte[] retval = new byte[4];
- String[] dots = ipv4Addr.getValue().split("\\.");
+ String[] dots = ipv4Addr.split("\\.");
for (int d = 0; d < 4; d++) {
retval[d] = (byte) (Short.parseShort(dots[d]) & 0xff);