9fc1a237f2bcb9ee9bffd136eaf5cc948713d27a
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / interfaces / acl / AceIp4Writer.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.translate.v3po.interfaces.acl;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.primitives.Ints;
23 import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
24 import javax.annotation.Nonnull;
25 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;
26 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;
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.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
29 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
30 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
31 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
32 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 final class AceIp4Writer extends AbstractAceWriter<AceIp> implements Ipv4Translator {
37
38     @VisibleForTesting
39     static final int MATCH_N_VECTORS = 3; // number of 16B vectors
40     private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class);
41     private static final int TABLE_MASK_LENGTH = 48;
42     private static final int IP4_MASK_BIT_LENGTH = 32;
43
44     private static final int IP_VERSION_OFFSET = 14; // first 14 bytes represent L2 header (2x6 + etherType(2))
45     private static final int IP_VERSION_MASK = 0xf0;
46     private static final int DSCP_OFFSET = 15;
47     private static final int DSCP_MASK = 0xfc;
48     private static final int IP4_LEN = 4;
49     private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 12;
50     private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN;
51
52     public AceIp4Writer(@Nonnull final FutureJVppCore futureJVppCore) {
53         super(futureJVppCore);
54     }
55
56     private static byte[] toByteMask(final int prefixLength) {
57         final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength);
58         return Ints.toByteArray((int) mask);
59     }
60
61     private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) {
62         final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]);
63         return toByteMask(prefixLength);
64     }
65
66     // static removed, cant use default from static content
67     private byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) {
68         final String[] split = ipv4Prefix.getValue().split("/");
69         final byte[] addressBytes = ipv4AddressNoZoneToArray(split[0]);
70         final byte[] mask = toByteMask(Byte.valueOf(split[1]));
71         for (int i = 0; i < addressBytes.length; ++i) {
72             addressBytes[i] &= mask[i];
73         }
74         return addressBytes;
75     }
76
77     @Override
78     public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
79                                                    @Nonnull final AceIp aceIp,
80                                                    final int nextTableIndex,
81                                                    final int vlanTags) {
82         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp);
83         final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion();
84
85         final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex);
86         request.skipNVectors = 0; // match entire L2 and L3 header
87         request.matchNVectors = MATCH_N_VECTORS;
88
89         boolean aceIsEmpty = true;
90         request.mask = new byte[TABLE_MASK_LENGTH];
91
92         final int baseOffset = getVlanTagsLen(vlanTags);
93
94         // First 14 bytes represent l2 header (2x6 + etherType(2))
95         if (aceIp.getProtocol() != null) { // Internet Protocol number
96             request.mask[baseOffset + IP_VERSION_OFFSET] = (byte) IP_VERSION_MASK; // first 4 bits
97         }
98
99         if (aceIp.getDscp() != null) {
100             aceIsEmpty = false;
101             request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits
102         }
103
104         if (aceIp.getSourcePortRange() != null) {
105             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
106         }
107
108         if (aceIp.getDestinationPortRange() != null) {
109             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
110         }
111
112         if (ipVersion.getSourceIpv4Network() != null) {
113             aceIsEmpty = false;
114             System.arraycopy(toByteMask(ipVersion.getSourceIpv4Network()), 0, request.mask, baseOffset + SRC_IP_OFFSET,
115                     IP4_LEN);
116         }
117
118         if (ipVersion.getDestinationIpv4Network() != null) {
119             aceIsEmpty = false;
120             System
121                     .arraycopy(toByteMask(ipVersion.getDestinationIpv4Network()), 0, request.mask,
122                             baseOffset + DST_IP_OFFSET, IP4_LEN);
123         }
124
125         if (aceIsEmpty) {
126             throw new IllegalArgumentException(
127                     String.format("Ace %s does not define packet field match values", aceIp.toString()));
128         }
129
130         LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceIp, request);
131         return request;
132     }
133
134     @Override
135     public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action,
136                                                        @Nonnull final AceIp aceIp,
137                                                        final int tableIndex,
138                                                        final int vlanTags) {
139         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp);
140         final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion();
141
142         final ClassifyAddDelSession request = createClassifySession(action, tableIndex);
143
144         request.match = new byte[TABLE_MASK_LENGTH];
145         boolean noMatch = true;
146
147         final int baseOffset = getVlanTagsLen(vlanTags);
148
149         if (aceIp.getProtocol() != null) {
150             request.match[baseOffset + IP_VERSION_OFFSET] =
151                     (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4));
152         }
153
154         if (aceIp.getDscp() != null) {
155             noMatch = false;
156             request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2));
157         }
158
159         if (aceIp.getSourcePortRange() != null) {
160             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
161         }
162
163         if (aceIp.getDestinationPortRange() != null) {
164             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
165         }
166
167         if (ipVersion.getSourceIpv4Network() != null) {
168             noMatch = false;
169             System
170                     .arraycopy(toMatchValue(ipVersion.getSourceIpv4Network()), 0, request.match,
171                             baseOffset + SRC_IP_OFFSET,
172                             IP4_LEN);
173         }
174
175         if (ipVersion.getDestinationIpv4Network() != null) {
176             noMatch = false;
177             System.arraycopy(toMatchValue(ipVersion.getDestinationIpv4Network()), 0, request.match,
178                     baseOffset + DST_IP_OFFSET,
179                     IP4_LEN);
180         }
181
182         if (noMatch) {
183             throw new IllegalArgumentException(
184                     String.format("Ace %s does not define packet field match values", aceIp.toString()));
185         }
186
187         LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request);
188         return request;
189     }
190
191     @Override
192     protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) {
193         request.ip4TableIndex = tableIndex;
194     }
195 }