Move interface acls to separate yang module
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / interfaces / acl / ingress / AceIp6Writer.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.ingress;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20
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.vpp.acl.rev161214.InterfaceMode;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 final class AceIp6Writer extends AbstractAceWriter<AceIp> {
40
41     @VisibleForTesting
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;
46
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;
55
56     public AceIp6Writer(@Nonnull final FutureJVppCore futureJVppCore) {
57         super(futureJVppCore);
58     }
59
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);
65         }
66         return mask.toByteArray();
67     }
68
69     private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) {
70         final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]);
71         return toByteMask(prefixLength);
72     }
73
74     private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) {
75         final String[] split = ipv6Prefix.getValue().split("/");
76         final byte[] addressBytes;
77         try {
78             addressBytes = InetAddress.getByName(split[0]).getAddress();
79         } catch (UnknownHostException e) {
80             throw new IllegalArgumentException("Invalid IP6 address", e);
81         }
82         final byte[] mask = toByteMask(Short.valueOf(split[1]));
83         int pos = 0;
84         for (; pos < mask.length; ++pos) {
85             addressBytes[pos] &= mask[pos];
86         }
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;
90         }
91         return addressBytes;
92     }
93
94     @Override
95     public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp,
96                                                    @Nullable final InterfaceMode mode,
97                                                    final int nextTableIndex,
98                                                    final int vlanTags) {
99         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp);
100         final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion();
101
102         final ClassifyAddDelTable request = createClassifyTable(nextTableIndex);
103         request.skipNVectors = 0; // match entire L2 and L3 header
104         request.matchNVectors = MATCH_N_VECTORS;
105
106         boolean aceIsEmpty = true;
107         request.mask = new byte[TABLE_MASK_LENGTH];
108
109         final int baseOffset = getVlanTagsLen(vlanTags);
110
111         if (InterfaceMode.L2.equals(mode)) {
112             // in L2 mode we need to match ether type
113             request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff;
114             request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff;
115         }
116
117         if (aceIp.getProtocol() != null) {
118             aceIsEmpty = false;
119             request.mask[baseOffset + IP_VERSION_OFFSET] |= IP_VERSION_MASK;
120         }
121
122         if (aceIp.getDscp() != null) {
123             aceIsEmpty = false;
124             // DCSP (bits 4-9 of IP6 header)
125             request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1;
126             request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2;
127         }
128
129         if (aceIp.getSourcePortRange() != null) {
130             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
131         }
132
133         if (aceIp.getDestinationPortRange() != null) {
134             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
135         }
136
137         if (ipVersion.getFlowLabel() != null) {
138             aceIsEmpty = false;
139             // bits 12-31
140             request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f;
141             request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff;
142             request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff;
143         }
144
145         if (ipVersion.getSourceIpv6Network() != null) {
146             aceIsEmpty = false;
147             final byte[] mask = toByteMask(ipVersion.getSourceIpv6Network());
148             System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length);
149         }
150
151         if (ipVersion.getDestinationIpv6Network() != null) {
152             aceIsEmpty = false;
153             final byte[] mask = toByteMask(ipVersion.getDestinationIpv6Network());
154             System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length);
155         }
156
157         if (aceIsEmpty) {
158             throw new IllegalArgumentException(
159                 String.format("Ace %s does not define packet field match values", aceIp.toString()));
160         }
161
162         LOG.debug("ACE rule={} translated to table={}.", aceIp, request);
163         return request;
164     }
165
166     @Override
167     public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action,
168                                                        @Nonnull final AceIp aceIp,
169                                                        @Nullable final InterfaceMode mode,
170                                                        final int tableIndex,
171                                                        final int vlanTags) {
172         checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp);
173         final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion();
174
175         final ClassifyAddDelSession request = createClassifySession(action, tableIndex);
176         request.match = new byte[TABLE_MASK_LENGTH];
177         boolean noMatch = true;
178
179         final int baseOffset = getVlanTagsLen(vlanTags);
180
181         if (InterfaceMode.L2.equals(mode)) {
182             // match IP6 etherType (0x86dd)
183             request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86;
184             request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd;
185         }
186
187         if (aceIp.getProtocol() != null) {
188             noMatch = false;
189             request.match[baseOffset + IP_VERSION_OFFSET] |=
190                 (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4));
191         }
192
193         if (aceIp.getDscp() != null) {
194             noMatch = false;
195             final int dscp = aceIp.getDscp().getValue();
196             // set bits 4-9 of IP6 header:
197             request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dscp >> 2));
198             request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dscp << 6));
199         }
200
201         if (aceIp.getSourcePortRange() != null) {
202             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange());
203         }
204
205         if (aceIp.getDestinationPortRange() != null) {
206             LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange());
207         }
208
209         if (ipVersion.getFlowLabel() != null) {
210             noMatch = false;
211             final int flowLabel = ipVersion.getFlowLabel().getValue().intValue();
212             // bits 12-31
213             request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16));
214             request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8));
215             request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel);
216         }
217
218         if (ipVersion.getSourceIpv6Network() != null) {
219             noMatch = false;
220             final byte[] match = toMatchValue(ipVersion.getSourceIpv6Network());
221             System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN);
222         }
223
224         if (ipVersion.getDestinationIpv6Network() != null) {
225             noMatch = false;
226             final byte[] match = toMatchValue(ipVersion.getDestinationIpv6Network());
227             System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN);
228         }
229
230         if (noMatch) {
231             throw new IllegalArgumentException(
232                 String.format("Ace %s does not define packet field match values", aceIp.toString()));
233         }
234
235         LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request);
236         return request;
237     }
238
239     @Override
240     protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) {
241         request.ip6TableIndex = tableIndex;
242     }
243 }