4796919969a055c3ba0bb3657ff7c3e40d9c784c
[netvirt.git] /
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.math.BigInteger;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.nio.ByteBuffer;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.List;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.ExecutionException;
22 import org.apache.commons.lang3.StringUtils;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.ActionType;
28 import org.opendaylight.genius.mdsalutil.FlowEntity;
29 import org.opendaylight.genius.mdsalutil.InstructionInfo;
30 import org.opendaylight.genius.mdsalutil.InstructionType;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.MatchFieldType;
33 import org.opendaylight.genius.mdsalutil.MatchInfo;
34 import org.opendaylight.genius.mdsalutil.NwConstants;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
37 import org.opendaylight.genius.utils.ServiceIndex;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
56 import org.opendaylight.yangtools.yang.binding.DataObject;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 public class Ipv6ServiceUtils {
62     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
63     private ConcurrentMap<String, InstanceIdentifier<Flow>> icmpv6FlowMap;
64     public static final Ipv6ServiceUtils INSTANCE = new Ipv6ServiceUtils();
65     public static Ipv6Address ALL_NODES_MCAST_ADDR;
66     public static Ipv6Address UNSPECIFIED_ADDR;
67
68     public Ipv6ServiceUtils() {
69         icmpv6FlowMap = new ConcurrentHashMap<>();
70         try {
71             UNSPECIFIED_ADDR = Ipv6Address.getDefaultInstance(
72                     InetAddress.getByName("0:0:0:0:0:0:0:0").getHostAddress());
73             ALL_NODES_MCAST_ADDR = Ipv6Address.getDefaultInstance(InetAddress.getByName("FF02::1").getHostAddress());
74         } catch (UnknownHostException e) {
75             LOG.error("Ipv6ServiceUtils: Failed to instantiate the ipv6 address", e);
76         }
77     }
78
79     public static Ipv6ServiceUtils getInstance() {
80         return INSTANCE;
81     }
82
83     /**
84      * Retrieves the object from the datastore.
85      * @param broker the data broker.
86      * @param datastoreType the data store type.
87      * @param path the wild card path.
88      * @return the required object.
89      */
90     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
91                                                           InstanceIdentifier<T> path) {
92         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
93         Optional<T> result = Optional.absent();
94         try {
95             result = tx.read(datastoreType, path).get();
96         } catch (InterruptedException | ExecutionException e) {
97             throw new RuntimeException(e);
98         } finally {
99             tx.close();
100         }
101         return result;
102     }
103
104     /**
105      * Retrieves the Interface from the datastore.
106      * @param broker the data broker
107      * @param interfaceName the interface name
108      * @return the interface.
109      */
110     public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
111         .Interface getInterface(DataBroker broker, String interfaceName) {
112         Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
113             .Interface> optInterface =
114                 read(broker, LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
115         if (optInterface.isPresent()) {
116             return optInterface.get();
117         }
118         return null;
119     }
120
121     /**
122      * Builds the interface identifier.
123      * @param interfaceName the interface name.
124      * @return the interface identifier.
125      */
126     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
127             .interfaces.Interface> getInterfaceIdentifier(String interfaceName) {
128         return InstanceIdentifier.builder(Interfaces.class)
129                 .child(
130                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
131                                 .Interface.class, new InterfaceKey(interfaceName)).build();
132     }
133
134     public String bytesToHexString(byte[] bytes) {
135         if (bytes == null) {
136             return "null";
137         }
138         StringBuffer buf = new StringBuffer();
139         for (int i = 0; i < bytes.length; i++) {
140             if (i > 0) {
141                 buf.append(":");
142             }
143             short u8byte = (short) (bytes[i] & 0xff);
144             String tmp = Integer.toHexString(u8byte);
145             if (tmp.length() == 1) {
146                 buf.append("0");
147             }
148             buf.append(tmp);
149         }
150         return buf.toString();
151     }
152
153     public byte[] bytesFromHexString(String values) {
154         String target = "";
155         if (values != null) {
156             target = values;
157         }
158         String[] octets = target.split(":");
159
160         byte[] ret = new byte[octets.length];
161         for (int i = 0; i < octets.length; i++) {
162             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
163         }
164         return ret;
165     }
166
167     public int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
168         long checksum = getSummation(ip6Hdr.getSourceIpv6());
169         checksum += getSummation(ip6Hdr.getDestinationIpv6());
170         checksum = normalizeChecksum(checksum);
171
172         checksum += ip6Hdr.getIpv6Length();
173         checksum += ip6Hdr.getNextHeader();
174
175         int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
176         long value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
177         checksum += value;
178         checksum = normalizeChecksum(checksum);
179         icmp6Offset += 2;
180
181         //move to icmp6 payload skipping the checksum field
182         icmp6Offset += 2;
183         int length = packet.length - icmp6Offset;
184         while (length > 1) {
185             value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
186             checksum += value;
187             checksum = normalizeChecksum(checksum);
188             icmp6Offset += 2;
189             length -= 2;
190         }
191
192         if (length > 0) {
193             checksum += packet[icmp6Offset];
194             checksum = normalizeChecksum(checksum);
195         }
196
197         int finalChecksum = (int)(~checksum & 0xffff);
198         return finalChecksum;
199     }
200
201     public boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
202         int checksum = calcIcmpv6Checksum(packet, ip6Hdr);
203
204         if (checksum == recvChecksum) {
205             return true;
206         }
207         return false;
208     }
209
210     private long getSummation(Ipv6Address addr) {
211         byte[] baddr = null;
212         try {
213             baddr = InetAddress.getByName(addr.getValue()).getAddress();
214         } catch (UnknownHostException e) {
215             LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
216         }
217
218         long sum = 0;
219         int len = 0;
220         long value = 0;
221         while (len < baddr.length) {
222             value = (((baddr[len] & 0xff) << 8) | (baddr[len + 1] & 0xff));
223             sum += value;
224             sum = normalizeChecksum(sum);
225             len += 2;
226         }
227         return sum;
228     }
229
230     private long normalizeChecksum(long value) {
231         if ((value & 0xffff0000) > 0) {
232             value = (value & 0xffff);
233             value += 1;
234         }
235         return value;
236     }
237
238     public byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
239         byte[] data = new byte[16];
240         Arrays.fill(data, (byte)0);
241
242         ByteBuffer buf = ByteBuffer.wrap(data);
243         buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue().toString()));
244         buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue().toString()));
245         buf.putShort((short)ethPdu.getEthertype().intValue());
246         return data;
247     }
248
249     public byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
250         byte[] data = new byte[128];
251         Arrays.fill(data, (byte)0);
252
253         ByteBuffer buf = ByteBuffer.wrap(data);
254         long flowLabel = (((long)(ip6Pdu.getVersion().shortValue() & 0x0f) << 28)
255                 | (ip6Pdu.getFlowLabel().longValue() & 0x0fffffff));
256         buf.putInt((int)flowLabel);
257         buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
258         buf.put((byte)ip6Pdu.getNextHeader().shortValue());
259         buf.put((byte)ip6Pdu.getHopLimit().shortValue());
260         try {
261             byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
262             buf.put(baddr);
263             baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
264             buf.put(baddr);
265         } catch (UnknownHostException e) {
266             LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
267         }
268         return data;
269     }
270
271     public Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
272         byte[] octets = bytesFromHexString(mac.getValue());
273
274         /* As per the RFC2373, steps involved to generate a LLA include
275            1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
276               between OUI and NIC Specific part.
277            2. Invert the Universal/Local flag in the OUI portion of the address.
278            3. Use the prefix "FE80::/10" along with the above 64 bit Interface
279               identifier to generate the IPv6 LLA. */
280
281         StringBuffer interfaceID = new StringBuffer();
282         short u8byte = (short) (octets[0] & 0xff);
283         u8byte ^= 1 << 1;
284         interfaceID.append(Integer.toHexString(0xFF & u8byte));
285         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
286         interfaceID.append(":");
287         interfaceID.append(Integer.toHexString(0xFF & octets[2]));
288         interfaceID.append("ff:fe");
289         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
290         interfaceID.append(":");
291         interfaceID.append(Integer.toHexString(0xFF & octets[4]));
292         interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
293
294         Ipv6Address ipv6LLA = new Ipv6Address("fe80:0:0:0:" + interfaceID.toString());
295         return ipv6LLA;
296     }
297
298     private static List<MatchInfo> getIcmpv6RSMatch(String vmMacAddress) {
299         List<MatchInfo> matches = new ArrayList<>();
300         matches.add(new MatchInfo(MatchFieldType.eth_src,
301                 new String[] { vmMacAddress }));
302         matches.add(new MatchInfo(MatchFieldType.eth_type,
303                 new long[] { NwConstants.ETHTYPE_IPV6 }));
304         matches.add(new MatchInfo(MatchFieldType.ip_proto,
305                 new long[] { IPProtocols.IPV6ICMP.intValue() }));
306         matches.add(new MatchInfo(MatchFieldType.icmp_v6,
307                 new long[] { Ipv6Constants.ICMP_V6_RS_CODE, 0}));
308         return matches;
309     }
310
311     private List<MatchInfo> getIcmpv6NSMatch(String vmMacAddress, String ndTarget) {
312         List<MatchInfo> matches = new ArrayList<>();
313         matches.add(new MatchInfo(MatchFieldType.eth_src,
314                 new String[] { vmMacAddress }));
315         matches.add(new MatchInfo(MatchFieldType.eth_type,
316                 new long[] { NwConstants.ETHTYPE_IPV6 }));
317         matches.add(new MatchInfo(MatchFieldType.ip_proto,
318                 new long[] { IPProtocols.IPV6ICMP.intValue() }));
319         matches.add(new MatchInfo(MatchFieldType.icmp_v6,
320                 new long[] { Ipv6Constants.ICMP_V6_NS_CODE, 0}));
321         matches.add(new MatchInfo(MatchFieldType.ipv6_nd_target,
322                 new String[] { ndTarget }));
323         return matches;
324     }
325
326     private static String getIPv6FlowRef(BigInteger dpId, String vmMacAddress, String flowType) {
327         return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
328                 .append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
329                 .append(vmMacAddress).append(flowType).toString();
330     }
331
332     private static void installRsPuntFlow(String interfaceName, short tableId, BigInteger dpId, String vmMacAddress,
333                                           IMdsalApiManager mdsalUtil, int addOrRemove) {
334         List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(vmMacAddress);
335         List<InstructionInfo> instructions = new ArrayList<>();
336         List<ActionInfo> actionsInfos = new ArrayList<>();
337         // Punt to controller
338         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller,
339                 new String[] {}));
340         instructions.add(new InstructionInfo(InstructionType.write_actions,
341                 actionsInfos));
342         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
343                 getIPv6FlowRef(dpId, vmMacAddress, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
344                 NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
345         if (addOrRemove == Ipv6Constants.DEL_FLOW) {
346             LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
347             mdsalUtil.removeFlow(rsFlowEntity);
348         } else {
349             LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
350             mdsalUtil.installFlow(rsFlowEntity);
351         }
352     }
353
354     public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId, String vmMacAddress,
355                                         String ipv6Address, IMdsalApiManager mdsalUtil, int addOrRemove) {
356         List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(vmMacAddress, ipv6Address);
357         List<InstructionInfo> instructions = new ArrayList<>();
358         List<ActionInfo> actionsInfos = new ArrayList<>();
359         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller,
360                 new String[] {}));
361         instructions.add(new InstructionInfo(InstructionType.write_actions,
362                 actionsInfos));
363         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
364                 getIPv6FlowRef(dpId, vmMacAddress, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
365                 0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
366         if (addOrRemove == Ipv6Constants.DEL_FLOW) {
367             LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
368             mdsalUtil.removeFlow(rsFlowEntity);
369         } else {
370             LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
371             mdsalUtil.installFlow(rsFlowEntity);
372         }
373     }
374
375     public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, String vmMacAddress,
376                                           IMdsalApiManager mdsalUtil, int addOrRemove) {
377         if (dpId == null || dpId.equals(Ipv6Constants.INVALID_DPID)) {
378             return;
379         }
380         List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(vmMacAddress);
381         List<InstructionInfo> instructions = new ArrayList<>();
382         List<ActionInfo> actionsInfos = new ArrayList<>();
383         // Punt to controller
384         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller,
385                 new String[] {}));
386         instructions.add(new InstructionInfo(InstructionType.write_actions,
387                 actionsInfos));
388         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
389                 getIPv6FlowRef(dpId, vmMacAddress, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
390                 NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
391         if (addOrRemove == Ipv6Constants.DEL_FLOW) {
392             LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
393             mdsalUtil.removeFlow(rsFlowEntity);
394         } else {
395             LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
396             mdsalUtil.installFlow(rsFlowEntity);
397         }
398     }
399
400     public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
401                                           BigInteger cookie, List<Instruction> instructions) {
402         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
403                 .setFlowPriority(flowPriority).setInstruction(instructions);
404         return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
405                 .setServiceName(serviceName).setServicePriority(servicePriority)
406                 .setServiceType(ServiceTypeFlowBased.class)
407                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
408     }
409
410     private InstanceIdentifier buildServiceId(String interfaceName,
411                                               short priority) {
412         return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
413                 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
414                 .child(BoundServices.class, new BoundServicesKey(priority)).build();
415     }
416
417     public void bindIpv6Service(DataBroker broker, String interfaceName, short tableId) {
418         int instructionKey = 0;
419         List<Instruction> instructions = new ArrayList<>();
420         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
421         short serviceIndex = ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME, NwConstants.IPV6_SERVICE_INDEX);
422         BoundServices
423                 serviceInfo =
424                 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
425                         serviceIndex, Ipv6Constants.DEFAULT_FLOW_PRIORITY,
426                         NwConstants.COOKIE_IPV6_TABLE, instructions);
427         MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
428                 buildServiceId(interfaceName, serviceIndex), serviceInfo);
429     }
430
431     public void unbindIpv6Service(DataBroker broker, String interfaceName) {
432         MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
433                 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
434                         NwConstants.IPV6_SERVICE_INDEX)));
435     }
436 }