c0da756879f7ea511cb49285cb71f9eeb574f827
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / interfaces / acl / common / Ip6AclTranslator.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.common;
18
19 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
20 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
21 import java.net.InetAddress;
22 import java.net.UnknownHostException;
23 import java.util.BitSet;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpHeaderFields;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv6HeaderFields;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode;
28
29 interface Ip6AclTranslator {
30
31     int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6)
32     int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET + 2;
33     int DSCP_MASK1 = 0x0f;
34     int DSCP_MASK2 = 0xc0;
35     int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET + 6;
36     int IP_PROTOCOL_MASK = 0xff;
37     int IP6_LEN = 16;
38     int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8;
39     int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN;
40     int SRC_PORT_OFFSET = DST_IP_OFFSET + IP6_LEN;
41     int DST_PORT_OFFSET = SRC_PORT_OFFSET + 2;
42
43     default boolean ip6Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header,
44                             final AclIpv6HeaderFields ip6, final ClassifyAddDelTable request) {
45         boolean aceIsEmpty = true;
46         if (InterfaceMode.L2.equals(mode)) {
47             // in L2 mode we need to match ether type
48             request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff;
49             request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff;
50         }
51         if (header.getDscp() != null) {
52             aceIsEmpty = false;
53             // DCSP (bits 4-9 of IP6 header)
54             request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1;
55             request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2;
56         }
57         if (header.getProtocol() != null) { // Internet Protocol number
58             aceIsEmpty = false;
59             request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK;
60         }
61         if (ip6.getFlowLabel() != null) {
62             aceIsEmpty = false;
63             // bits 12-31
64             request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f;
65             request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff;
66             request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff;
67         }
68         if (header.getSourcePortRange() != null) {
69             // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
70             aceIsEmpty = false;
71             request.mask[baseOffset + SRC_PORT_OFFSET] = (byte) 0xff;
72             request.mask[baseOffset + SRC_PORT_OFFSET + 1] = (byte) 0xff;
73         }
74         if (header.getDestinationPortRange() != null) {
75             // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
76             aceIsEmpty = false;
77             request.mask[baseOffset + DST_PORT_OFFSET] = (byte) 0xff;
78             request.mask[baseOffset + DST_PORT_OFFSET + 1] = (byte) 0xff;
79         }
80         if (ip6.getSourceIpv6Network() != null) {
81             aceIsEmpty = false;
82             final byte[] mask = Impl.toByteMask(ip6.getSourceIpv6Network());
83             System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length);
84         }
85         if (ip6.getDestinationIpv6Network() != null) {
86             aceIsEmpty = false;
87             final byte[] mask = Impl.toByteMask(ip6.getDestinationIpv6Network());
88             System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length);
89         }
90         return aceIsEmpty;
91     }
92
93     default boolean ip6Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header,
94                              final AclIpv6HeaderFields ip6, final Integer srcPort, final Integer dstPort, final ClassifyAddDelSession request) {
95         boolean noMatch = true;
96         if (InterfaceMode.L2.equals(mode)) {
97             // match IP6 etherType (0x86dd)
98             request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86;
99             request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd;
100         }
101         if (header.getDscp() != null) {
102             noMatch = false;
103             final int dcsp = header.getDscp().getValue();
104             // set bits 4-9 of IP6 header:
105             request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dcsp >> 2));
106             request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dcsp << 6));
107         }
108         if (header.getProtocol() != null) { // Internet Protocol number
109             noMatch = false;
110             request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol());
111         }
112         if (ip6.getFlowLabel() != null) {
113             noMatch = false;
114             final int flowLabel = ip6.getFlowLabel().getValue().intValue();
115             // bits 12-31
116             request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16));
117             request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8));
118             request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel);
119         }
120         if (header.getSourcePortRange() != null) {
121             // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
122             noMatch = false;
123             request.match[baseOffset + SRC_PORT_OFFSET] = (byte) (0xff & srcPort >> 8);
124             request.match[baseOffset + SRC_PORT_OFFSET + 1] = (byte) (0xff & srcPort);
125         }
126         if (header.getDestinationPortRange() != null) {
127             // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
128             noMatch = false;
129             request.match[baseOffset + DST_PORT_OFFSET] = (byte) (0xff & dstPort >> 8);
130             request.match[baseOffset + DST_PORT_OFFSET + 1] = (byte) (0xff & dstPort);
131         }
132         if (ip6.getSourceIpv6Network() != null) {
133             noMatch = false;
134             final byte[] match = Impl.toMatchValue(ip6.getSourceIpv6Network());
135             System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN);
136         }
137         if (ip6.getDestinationIpv6Network() != null) {
138             noMatch = false;
139             final byte[] match = Impl.toMatchValue(ip6.getDestinationIpv6Network());
140             System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN);
141         }
142         return noMatch;
143     }
144
145     class Impl {
146         private static final int IP6_MASK_BIT_LENGTH = 128;
147
148         private static byte[] toByteMask(final int prefixLength) {
149             final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH);
150             mask.set(0, prefixLength, true);
151             if (prefixLength < IP6_MASK_BIT_LENGTH) {
152                 mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false);
153             }
154             return mask.toByteArray();
155         }
156
157         private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) {
158             final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]);
159             return toByteMask(prefixLength);
160         }
161
162         private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) {
163             final String[] split = ipv6Prefix.getValue().split("/");
164             final byte[] addressBytes;
165             try {
166                 addressBytes = InetAddress.getByName(split[0]).getAddress();
167             } catch (UnknownHostException e) {
168                 throw new IllegalArgumentException("Invalid IP6 address", e);
169             }
170             final byte[] mask = toByteMask(Short.valueOf(split[1]));
171             int pos = 0;
172             for (; pos < mask.length; ++pos) {
173                 addressBytes[pos] &= mask[pos];
174             }
175             // mask can be shorter that address, so we need to clear rest of the address:
176             for (; pos < addressBytes.length; ++pos) {
177                 addressBytes[pos] = 0;
178             }
179             return addressBytes;
180         }
181     }
182 }