2 * Copyright © 2016, 2017 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.ipv6service.utils;
11 import com.google.common.base.Optional;
12 import com.google.common.net.InetAddresses;
13 import java.math.BigInteger;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.nio.ByteBuffer;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.apache.commons.lang3.StringUtils;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.mdsalutil.ActionInfo;
28 import org.opendaylight.genius.mdsalutil.FlowEntity;
29 import org.opendaylight.genius.mdsalutil.InstructionInfo;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.genius.mdsalutil.MatchInfo;
32 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
38 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
39 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
40 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6NdTarget;
41 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
42 import org.opendaylight.genius.utils.ServiceIndex;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
62 import org.opendaylight.yangtools.yang.binding.DataObject;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 public class Ipv6ServiceUtils {
69 private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
70 public static final Ipv6Address ALL_NODES_MCAST_ADDR = newIpv6Address("FF02::1");
71 public static final Ipv6Address UNSPECIFIED_ADDR = newIpv6Address("0:0:0:0:0:0:0:0");
73 private static Ipv6Address newIpv6Address(String ip) {
75 return Ipv6Address.getDefaultInstance(InetAddress.getByName(ip).getHostAddress());
76 } catch (UnknownHostException e) {
77 LOG.error("Ipv6ServiceUtils: Error instantiating ipv6 address", e);
82 private final DataBroker broker;
83 private final IMdsalApiManager mdsalUtil;
86 public Ipv6ServiceUtils(DataBroker broker, IMdsalApiManager mdsalUtil) {
88 this.mdsalUtil = mdsalUtil;
92 * Retrieves the object from the datastore.
93 * @param datastoreType the data store type.
94 * @param path the wild card path.
95 * @return the required object.
97 public <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
98 try (ReadOnlyTransaction tx = broker.newReadOnlyTransaction()) {
99 return tx.read(datastoreType, path).get();
100 } catch (InterruptedException | ExecutionException e) {
101 throw new RuntimeException(e);
106 * Retrieves the Interface from the datastore.
107 * @param interfaceName the interface name
108 * @return the interface.
110 public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
111 .Interface getInterface(String interfaceName) {
112 Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
113 .Interface> optInterface =
114 read(LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
115 if (optInterface.isPresent()) {
116 return optInterface.get();
122 * Builds the interface identifier.
123 * @param interfaceName the interface name.
124 * @return the interface identifier.
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)
130 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
131 .Interface.class, new InterfaceKey(interfaceName)).build();
135 * Build the interface state.
136 * @param interfaceName the interface name.
137 * @return the interface state.
139 public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
140 .interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
141 InstanceIdentifier.InstanceIdentifierBuilder<Interface> idBuilder =
142 InstanceIdentifier.builder(InterfacesState.class)
143 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
144 .state.Interface.class,
145 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
146 .rev140508.interfaces.state.InterfaceKey(interfaceName));
147 return idBuilder.build();
151 * Retrieves the interface state.
152 * @param interfaceName the interface name.
153 * @return the interface state.
155 public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
156 .Interface getInterfaceStateFromOperDS(String interfaceName) {
157 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
158 .interfaces.state.Interface> ifStateId = buildStateInterfaceId(interfaceName);
159 return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, ifStateId, broker).orNull();
162 public static String bytesToHexString(byte[] bytes) {
166 StringBuilder buf = new StringBuilder();
167 for (int i = 0; i < bytes.length; i++) {
171 short u8byte = (short) (bytes[i] & 0xff);
172 String tmp = Integer.toHexString(u8byte);
173 if (tmp.length() == 1) {
178 return buf.toString();
181 public static byte[] bytesFromHexString(String values) {
183 if (values != null) {
186 String[] octets = target.split(":");
188 byte[] ret = new byte[octets.length];
189 for (int i = 0; i < octets.length; i++) {
190 ret[i] = Integer.valueOf(octets[i], 16).byteValue();
195 public static int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
196 long checksum = getSummation(ip6Hdr.getSourceIpv6());
197 checksum += getSummation(ip6Hdr.getDestinationIpv6());
198 checksum = normalizeChecksum(checksum);
200 checksum += ip6Hdr.getIpv6Length();
201 checksum += ip6Hdr.getNextHeader();
203 int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
204 long value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
206 checksum = normalizeChecksum(checksum);
209 //move to icmp6 payload skipping the checksum field
211 int length = packet.length - icmp6Offset;
213 value = (packet[icmp6Offset] & 0xff) << 8 | packet[icmp6Offset + 1] & 0xff;
215 checksum = normalizeChecksum(checksum);
221 checksum += packet[icmp6Offset];
222 checksum = normalizeChecksum(checksum);
225 int finalChecksum = (int)(~checksum & 0xffff);
226 return finalChecksum;
229 public static boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
230 return calcIcmpv6Checksum(packet, ip6Hdr) == recvChecksum;
233 private static long getSummation(Ipv6Address addr) {
236 baddr = InetAddress.getByName(addr.getValue()).getAddress();
237 } catch (UnknownHostException e) {
238 LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
245 while (len < baddr.length) {
246 value = (baddr[len] & 0xff) << 8 | baddr[len + 1] & 0xff;
248 sum = normalizeChecksum(sum);
254 private static long normalizeChecksum(long value) {
255 if ((value & 0xffff0000) != 0) {
256 value = value & 0xffff;
262 public static byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
263 byte[] data = new byte[16];
264 Arrays.fill(data, (byte)0);
266 ByteBuffer buf = ByteBuffer.wrap(data);
267 buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue()));
268 buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue()));
269 buf.putShort((short)ethPdu.getEthertype().intValue());
273 public static byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
274 byte[] data = new byte[128];
275 Arrays.fill(data, (byte)0);
277 ByteBuffer buf = ByteBuffer.wrap(data);
278 long flowLabel = (long)(ip6Pdu.getVersion() & 0x0f) << 28
279 | ip6Pdu.getFlowLabel() & 0x0fffffff;
280 buf.putInt((int)flowLabel);
281 buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
282 buf.put((byte)ip6Pdu.getNextHeader().shortValue());
283 buf.put((byte)ip6Pdu.getHopLimit().shortValue());
285 byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
287 baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
289 } catch (UnknownHostException e) {
290 LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
295 public static Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
296 byte[] octets = bytesFromHexString(mac.getValue());
298 /* As per the RFC2373, steps involved to generate a LLA include
299 1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
300 between OUI and NIC Specific part.
301 2. Invert the Universal/Local flag in the OUI portion of the address.
302 3. Use the prefix "FE80::/10" along with the above 64 bit Interface
303 identifier to generate the IPv6 LLA. */
305 StringBuilder interfaceID = new StringBuilder();
306 short u8byte = (short) (octets[0] & 0xff);
308 interfaceID.append(Integer.toHexString(0xFF & u8byte));
309 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
310 interfaceID.append(":");
311 interfaceID.append(Integer.toHexString(0xFF & octets[2]));
312 interfaceID.append("ff:fe");
313 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
314 interfaceID.append(":");
315 interfaceID.append(Integer.toHexString(0xFF & octets[4]));
316 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
318 // Return the address in its fully expanded format.
319 Ipv6Address ipv6LLA = new Ipv6Address(InetAddresses.forString(
320 "fe80:0:0:0:" + interfaceID.toString()).getHostAddress());
324 public static Ipv6Address getIpv6SolicitedNodeMcastAddress(Ipv6Address ipv6Address) {
326 /* According to RFC 4291, a Solicited Node Multicast Address is derived by adding the 24
327 lower order bits with the Solicited Node multicast prefix (i.e., FF02::1:FF00:0/104).
328 Example: For IPv6Address of FE80::2AA:FF:FE28:9C5A, the corresponding solicited node
329 multicast address would be FF02::1:FF28:9C5A
334 octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
335 } catch (UnknownHostException e) {
336 LOG.error("getIpv6SolicitedNodeMcastAddress: Failed to serialize ipv6Address ", e);
340 // Return the address in its fully expanded format.
341 Ipv6Address solictedV6Address = new Ipv6Address(InetAddresses.forString("ff02::1:ff"
342 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0") + ":"
343 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0")
344 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0")).getHostAddress());
346 return solictedV6Address;
349 public static MacAddress getIpv6MulticastMacAddress(Ipv6Address ipv6Address) {
351 /* According to RFC 2464, a Multicast MAC address is derived by concatenating 32 lower
352 order bits of IPv6 Multicast Address with the multicast prefix (i.e., 33:33).
353 Example: For Multicast IPv6Address of FF02::1:FF28:9C5A, the corresponding L2 multicast
354 address would be 33:33:28:9C:5A
358 octets = InetAddress.getByName(ipv6Address.getValue()).getAddress();
359 } catch (UnknownHostException e) {
360 LOG.error("getIpv6MulticastMacAddress: Failed to serialize ipv6Address ", e);
364 String macAddress = "33:33:"
365 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[12]), 2, "0") + ":"
366 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[13]), 2, "0") + ":"
367 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[14]), 2, "0") + ":"
368 + StringUtils.leftPad(Integer.toHexString(0xFF & octets[15]), 2, "0");
370 return new MacAddress(macAddress);
373 private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
374 List<MatchInfo> matches = new ArrayList<>();
375 matches.add(MatchEthernetType.IPV6);
376 matches.add(MatchIpProtocol.ICMPV6);
377 matches.add(new MatchIcmpv6(Ipv6Constants.ICMP_V6_RS_CODE, (short) 0));
378 matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
382 private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, String ndTarget) {
383 List<MatchInfo> matches = new ArrayList<>();
384 matches.add(MatchEthernetType.IPV6);
385 matches.add(MatchIpProtocol.ICMPV6);
386 matches.add(new MatchIcmpv6(Ipv6Constants.ICMP_V6_NS_CODE, (short) 0));
387 matches.add(new MatchIpv6NdTarget(new Ipv6Address(ndTarget)));
388 matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
392 private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
393 return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
394 .append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
395 .append(elanTag).append(Ipv6Constants.FLOWID_SEPARATOR)
396 .append(flowType).toString();
399 public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId, Long elanTag, String ipv6Address,
401 List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
402 List<InstructionInfo> instructions = new ArrayList<>();
403 List<ActionInfo> actionsInfos = new ArrayList<>();
404 actionsInfos.add(new ActionPuntToController());
405 instructions.add(new InstructionApplyActions(actionsInfos));
406 FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
407 getIPv6FlowRef(dpId, elanTag, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
408 0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
409 if (addOrRemove == Ipv6Constants.DEL_FLOW) {
410 LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
411 mdsalUtil.removeFlow(rsFlowEntity);
413 LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
414 mdsalUtil.installFlow(rsFlowEntity);
418 public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, Long elanTag, int addOrRemove) {
419 if (dpId == null || dpId.equals(Ipv6Constants.INVALID_DPID)) {
422 List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
423 List<InstructionInfo> instructions = new ArrayList<>();
424 List<ActionInfo> actionsInfos = new ArrayList<>();
425 // Punt to controller
426 actionsInfos.add(new ActionPuntToController());
427 instructions.add(new InstructionApplyActions(actionsInfos));
428 FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
429 getIPv6FlowRef(dpId, elanTag, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
430 NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
431 if (addOrRemove == Ipv6Constants.DEL_FLOW) {
432 LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
433 mdsalUtil.removeFlow(rsFlowEntity);
435 LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
436 mdsalUtil.installFlow(rsFlowEntity);
440 public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
441 BigInteger cookie, List<Instruction> instructions) {
442 StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
443 .setFlowPriority(flowPriority).setInstruction(instructions);
444 return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority))
445 .setServiceName(serviceName).setServicePriority(servicePriority)
446 .setServiceType(ServiceTypeFlowBased.class)
447 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
450 private InstanceIdentifier<BoundServices> buildServiceId(String interfaceName,
452 return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
453 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
454 .child(BoundServices.class, new BoundServicesKey(priority)).build();
457 public void bindIpv6Service(String interfaceName, Long elanTag, short tableId) {
458 int instructionKey = 0;
459 List<Instruction> instructions = new ArrayList<>();
460 instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getElanTagMetadata(elanTag),
461 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
462 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
463 short serviceIndex = ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME, NwConstants.IPV6_SERVICE_INDEX);
466 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
467 serviceIndex, Ipv6Constants.DEFAULT_FLOW_PRIORITY,
468 NwConstants.COOKIE_IPV6_TABLE, instructions);
469 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
470 buildServiceId(interfaceName, serviceIndex), serviceInfo);
473 public void unbindIpv6Service(String interfaceName) {
474 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
475 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
476 NwConstants.IPV6_SERVICE_INDEX)));