2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
19 import static com.google.common.base.Preconditions.checkArgument;
21 import com.google.common.annotations.VisibleForTesting;
22 import java.net.InetAddress;
23 import java.net.UnknownHostException;
24 import java.util.BitSet;
25 import javax.annotation.Nonnull;
26 import javax.annotation.Nullable;
27 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;
28 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;
29 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.AceIpv6;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
31 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
32 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
33 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
34 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.InterfaceMode;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 final class AceIp6Writer extends AbstractAceWriter<AceIp> {
42 static final int MATCH_N_VECTORS = 4; // number of 16B vectors
43 private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class);
44 private static final int TABLE_MASK_LENGTH = 64;
45 private static final int IP6_MASK_BIT_LENGTH = 128;
47 private static final int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6)
48 private static final int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET+2;
49 private static final int IP_VERSION_MASK = 0xf0;
50 private static final int DSCP_MASK1 = 0x0f;
51 private static final int DSCP_MASK2 = 0xc0;
52 private static final int IP6_LEN = 16;
53 private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8;
54 private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN;
56 public AceIp6Writer(@Nonnull final FutureJVppCore futureJVppCore) {
57 super(futureJVppCore);
60 private static byte[] toByteMask(final int prefixLength) {
61 final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH);
62 mask.set(0, prefixLength, true);
63 if (prefixLength < IP6_MASK_BIT_LENGTH) {
64 mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false);
66 return mask.toByteArray();
69 private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) {
70 final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]);
71 return toByteMask(prefixLength);
74 private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) {
75 final String[] split = ipv6Prefix.getValue().split("/");
76 final byte[] addressBytes;
78 addressBytes = InetAddress.getByName(split[0]).getAddress();
79 } catch (UnknownHostException e) {
80 throw new IllegalArgumentException("Invalid IP6 address", e);
82 final byte[] mask = toByteMask(Short.valueOf(split[1]));
84 for (; pos < mask.length; ++pos) {
85 addressBytes[pos] &= mask[pos];
87 // mask can be shorter that address, so we need to clear rest of the address:
88 for (; pos < addressBytes.length; ++pos) {
89 addressBytes[pos] = 0;
95 public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
96 @Nonnull final AceIp aceIp,
97 @Nullable final InterfaceMode mode,
98 final int nextTableIndex,
100 checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp);
101 final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion();
103 final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex);
104 request.skipNVectors = 0; // match entire L2 and L3 header
105 request.matchNVectors = MATCH_N_VECTORS;
107 boolean aceIsEmpty = true;
108 request.mask = new byte[TABLE_MASK_LENGTH];
110 final int baseOffset = getVlanTagsLen(vlanTags);
112 if (InterfaceMode.L2.equals(mode)) {
113 // in L2 mode we need to match ether type
114 request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff;
115 request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff;
118 if (aceIp.getProtocol() != null) {
120 request.mask[baseOffset + IP_VERSION_OFFSET] |= IP_VERSION_MASK;
123 if (aceIp.getDscp() != null) {
125 // DCSP (bits 4-9 of IP6 header)
126 request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1;
127 request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2;
130 if (aceIp.getSourcePortRange() != null) {
131 LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
134 if (aceIp.getDestinationPortRange() != null) {
135 LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
138 if (ipVersion.getFlowLabel() != null) {
141 request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f;
142 request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff;
143 request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff;
146 if (ipVersion.getSourceIpv6Network() != null) {
148 final byte[] mask = toByteMask(ipVersion.getSourceIpv6Network());
149 System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length);
152 if (ipVersion.getDestinationIpv6Network() != null) {
154 final byte[] mask = toByteMask(ipVersion.getDestinationIpv6Network());
155 System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length);
159 throw new IllegalArgumentException(
160 String.format("Ace %s does not define packet field match values", aceIp.toString()));
163 LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceIp, request);
168 public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action,
169 @Nonnull final AceIp aceIp,
170 @Nullable final InterfaceMode mode,
171 final int tableIndex,
172 final int vlanTags) {
173 checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp);
174 final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion();
176 final ClassifyAddDelSession request = createClassifySession(action, tableIndex);
177 request.match = new byte[TABLE_MASK_LENGTH];
178 boolean noMatch = true;
180 final int baseOffset = getVlanTagsLen(vlanTags);
182 if (InterfaceMode.L2.equals(mode)) {
183 // match IP6 etherType (0x86dd)
184 request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86;
185 request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd;
188 if (aceIp.getProtocol() != null) {
190 request.match[baseOffset + IP_VERSION_OFFSET] |=
191 (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4));
194 if (aceIp.getDscp() != null) {
196 final int dscp = aceIp.getDscp().getValue();
197 // set bits 4-9 of IP6 header:
198 request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dscp >> 2));
199 request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dscp << 6));
202 if (aceIp.getSourcePortRange() != null) {
203 LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
206 if (aceIp.getDestinationPortRange() != null) {
207 LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
210 if (ipVersion.getFlowLabel() != null) {
212 final int flowLabel = ipVersion.getFlowLabel().getValue().intValue();
214 request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16));
215 request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8));
216 request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel);
219 if (ipVersion.getSourceIpv6Network() != null) {
221 final byte[] match = toMatchValue(ipVersion.getSourceIpv6Network());
222 System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN);
225 if (ipVersion.getDestinationIpv6Network() != null) {
227 final byte[] match = toMatchValue(ipVersion.getDestinationIpv6Network());
228 System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN);
232 throw new IllegalArgumentException(
233 String.format("Ace %s does not define packet field match values", aceIp.toString()));
236 LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request);
241 protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) {
242 request.ip6TableIndex = tableIndex;