Support Neighbor Advertisement functionality for router iface
[netvirt.git] / vpnservice / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / utils / Ipv6ServiceUtils.java
1 /*
2  * Copyright (c) 2016 Red Hat, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netvirt.ipv6service.utils;
10
11 import com.google.common.base.Optional;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.nio.ByteBuffer;
15 import java.util.Arrays;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18 import java.util.concurrent.ExecutionException;
19 import org.apache.commons.lang3.StringUtils;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public class Ipv6ServiceUtils {
36     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
37     private ConcurrentMap<String, InstanceIdentifier<Flow>> icmpv6FlowMap;
38     public static final Ipv6ServiceUtils instance = new Ipv6ServiceUtils();
39     public static Ipv6Address ALL_NODES_MCAST_ADDR;
40     public static Ipv6Address UNSPECIFIED_ADDR;
41
42     public Ipv6ServiceUtils() {
43         icmpv6FlowMap = new ConcurrentHashMap<>();
44         try {
45             UNSPECIFIED_ADDR = Ipv6Address.getDefaultInstance(
46                     InetAddress.getByName("0:0:0:0:0:0:0:0").getHostAddress());
47             ALL_NODES_MCAST_ADDR = Ipv6Address.getDefaultInstance(InetAddress.getByName("FF02::1").getHostAddress());
48         } catch (UnknownHostException e) {
49             LOG.error("Ipv6ServiceUtils: Failed to instantiate the ipv6 address", e);
50         }
51     }
52
53     public static Ipv6ServiceUtils getInstance() {
54         return instance;
55     }
56
57     /**
58      * Retrieves the object from the datastore.
59      * @param broker the data broker.
60      * @param datastoreType the data store type.
61      * @param path the wild card path.
62      * @return the required object.
63      */
64     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
65                                                           InstanceIdentifier<T> path) {
66         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
67         Optional<T> result = Optional.absent();
68         try {
69             result = tx.read(datastoreType, path).get();
70         } catch (InterruptedException | ExecutionException e) {
71             throw new RuntimeException(e);
72         } finally {
73             tx.close();
74         }
75         return result;
76     }
77
78     /**
79      * Retrieves the Interface from the datastore.
80      * @param broker the data broker
81      * @param interfaceName the interface name
82      * @return the interface.
83      */
84     public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
85         .Interface getInterface(DataBroker broker, String interfaceName) {
86         Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
87             .Interface> optInterface =
88                 read(broker, LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
89         if (optInterface.isPresent()) {
90             return optInterface.get();
91         }
92         return null;
93     }
94
95     /**
96      * Builds the interface identifier.
97      * @param interfaceName the interface name.
98      * @return the interface identifier.
99      */
100     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
101             .interfaces.Interface> getInterfaceIdentifier(String interfaceName) {
102         return InstanceIdentifier.builder(Interfaces.class)
103                 .child(
104                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
105                                 .Interface.class, new InterfaceKey(interfaceName)).build();
106     }
107
108     public String bytesToHexString(byte[] bytes) {
109         if (bytes == null) {
110             return "null";
111         }
112         StringBuffer buf = new StringBuffer();
113         for (int i = 0; i < bytes.length; i++) {
114             if (i > 0) {
115                 buf.append(":");
116             }
117             short u8byte = (short) (bytes[i] & 0xff);
118             String tmp = Integer.toHexString(u8byte);
119             if (tmp.length() == 1) {
120                 buf.append("0");
121             }
122             buf.append(tmp);
123         }
124         return buf.toString();
125     }
126
127     public byte[] bytesFromHexString(String values) {
128         String target = "";
129         if (values != null) {
130             target = values;
131         }
132         String[] octets = target.split(":");
133
134         byte[] ret = new byte[octets.length];
135         for (int i = 0; i < octets.length; i++) {
136             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
137         }
138         return ret;
139     }
140
141     public int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
142         long checksum = getSummation(ip6Hdr.getSourceIpv6());
143         checksum += getSummation(ip6Hdr.getDestinationIpv6());
144         checksum = normalizeChecksum(checksum);
145
146         checksum += ip6Hdr.getIpv6Length();
147         checksum += ip6Hdr.getNextHeader();
148
149         int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
150         long value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
151         checksum += value;
152         checksum = normalizeChecksum(checksum);
153         icmp6Offset += 2;
154
155         //move to icmp6 payload skipping the checksum field
156         icmp6Offset += 2;
157         int length = packet.length - icmp6Offset;
158         while (length > 1) {
159             value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
160             checksum += value;
161             checksum = normalizeChecksum(checksum);
162             icmp6Offset += 2;
163             length -= 2;
164         }
165
166         if (length > 0) {
167             checksum += packet[icmp6Offset];
168             checksum = normalizeChecksum(checksum);
169         }
170
171         int finalChecksum = (int)(~checksum & 0xffff);
172         return finalChecksum;
173     }
174
175     public boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
176         int checksum = calcIcmpv6Checksum(packet, ip6Hdr);
177
178         if (checksum == recvChecksum) {
179             return true;
180         }
181         return false;
182     }
183
184     private long getSummation(Ipv6Address addr) {
185         byte[] baddr = null;
186         try {
187             baddr = InetAddress.getByName(addr.getValue()).getAddress();
188         } catch (UnknownHostException e) {
189             LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
190         }
191
192         long sum = 0;
193         int len = 0;
194         long value = 0;
195         while (len < baddr.length) {
196             value = (((baddr[len] & 0xff) << 8) | (baddr[len + 1] & 0xff));
197             sum += value;
198             sum = normalizeChecksum(sum);
199             len += 2;
200         }
201         return sum;
202     }
203
204     private long normalizeChecksum(long value) {
205         if ((value & 0xffff0000) > 0) {
206             value = (value & 0xffff);
207             value += 1;
208         }
209         return value;
210     }
211
212     public byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
213         byte[] data = new byte[16];
214         Arrays.fill(data, (byte)0);
215
216         ByteBuffer buf = ByteBuffer.wrap(data);
217         buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue().toString()));
218         buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue().toString()));
219         buf.putShort((short)ethPdu.getEthertype().intValue());
220         return data;
221     }
222
223     public byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
224         byte[] data = new byte[128];
225         Arrays.fill(data, (byte)0);
226
227         ByteBuffer buf = ByteBuffer.wrap(data);
228         long flowLabel = (((long)(ip6Pdu.getVersion().shortValue() & 0x0f) << 28)
229                 | (ip6Pdu.getFlowLabel().longValue() & 0x0fffffff));
230         buf.putInt((int)flowLabel);
231         buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
232         buf.put((byte)ip6Pdu.getNextHeader().shortValue());
233         buf.put((byte)ip6Pdu.getHopLimit().shortValue());
234         try {
235             byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
236             buf.put(baddr);
237             baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
238             buf.put(baddr);
239         } catch (UnknownHostException e) {
240             LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
241         }
242         return data;
243     }
244
245     public Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
246         byte[] octets = bytesFromHexString(mac.getValue());
247
248         /* As per the RFC2373, steps involved to generate a LLA include
249            1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
250               between OUI and NIC Specific part.
251            2. Invert the Universal/Local flag in the OUI portion of the address.
252            3. Use the prefix "FE80::/10" along with the above 64 bit Interface
253               identifier to generate the IPv6 LLA. */
254
255         StringBuffer interfaceID = new StringBuffer();
256         short u8byte = (short) (octets[0] & 0xff);
257         u8byte ^= 1 << 1;
258         interfaceID.append(Integer.toHexString(0xFF & u8byte));
259         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
260         interfaceID.append(":");
261         interfaceID.append(Integer.toHexString(0xFF & octets[2]));
262         interfaceID.append("ff:fe");
263         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
264         interfaceID.append(":");
265         interfaceID.append(Integer.toHexString(0xFF & octets[4]));
266         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
267
268         Ipv6Address ipv6LLA = new Ipv6Address("fe80:0:0:0:" + interfaceID.toString());
269         return ipv6LLA;
270     }
271 }