2 * Copyright (c) 2016 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 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.MetaDataUtil;
35 import org.opendaylight.genius.mdsalutil.NwConstants;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
38 import org.opendaylight.genius.utils.ServiceIndex;
39 import org.opendaylight.netvirt.elan.utils.ElanUtils;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
58 import org.opendaylight.yangtools.yang.binding.DataObject;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
63 public class Ipv6ServiceUtils {
64 private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
65 private ConcurrentMap<String, InstanceIdentifier<Flow>> icmpv6FlowMap;
66 public static final Ipv6ServiceUtils INSTANCE = new Ipv6ServiceUtils();
67 public static Ipv6Address ALL_NODES_MCAST_ADDR;
68 public static Ipv6Address UNSPECIFIED_ADDR;
70 public Ipv6ServiceUtils() {
71 icmpv6FlowMap = new ConcurrentHashMap<>();
73 UNSPECIFIED_ADDR = Ipv6Address.getDefaultInstance(
74 InetAddress.getByName("0:0:0:0:0:0:0:0").getHostAddress());
75 ALL_NODES_MCAST_ADDR = Ipv6Address.getDefaultInstance(InetAddress.getByName("FF02::1").getHostAddress());
76 } catch (UnknownHostException e) {
77 LOG.error("Ipv6ServiceUtils: Failed to instantiate the ipv6 address", e);
81 public static Ipv6ServiceUtils getInstance() {
86 * Retrieves the object from the datastore.
87 * @param broker the data broker.
88 * @param datastoreType the data store type.
89 * @param path the wild card path.
90 * @return the required object.
92 public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
93 InstanceIdentifier<T> path) {
94 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
95 Optional<T> result = Optional.absent();
97 result = tx.read(datastoreType, path).get();
98 } catch (InterruptedException | ExecutionException e) {
99 throw new RuntimeException(e);
107 * Retrieves the Interface from the datastore.
108 * @param broker the data broker
109 * @param interfaceName the interface name
110 * @return the interface.
112 public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
113 .Interface getInterface(DataBroker broker, String interfaceName) {
114 Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
115 .Interface> optInterface =
116 read(broker, LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName));
117 if (optInterface.isPresent()) {
118 return optInterface.get();
124 * Builds the interface identifier.
125 * @param interfaceName the interface name.
126 * @return the interface identifier.
128 public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
129 .interfaces.Interface> getInterfaceIdentifier(String interfaceName) {
130 return InstanceIdentifier.builder(Interfaces.class)
132 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
133 .Interface.class, new InterfaceKey(interfaceName)).build();
136 public String bytesToHexString(byte[] bytes) {
140 StringBuffer buf = new StringBuffer();
141 for (int i = 0; i < bytes.length; i++) {
145 short u8byte = (short) (bytes[i] & 0xff);
146 String tmp = Integer.toHexString(u8byte);
147 if (tmp.length() == 1) {
152 return buf.toString();
155 public byte[] bytesFromHexString(String values) {
157 if (values != null) {
160 String[] octets = target.split(":");
162 byte[] ret = new byte[octets.length];
163 for (int i = 0; i < octets.length; i++) {
164 ret[i] = Integer.valueOf(octets[i], 16).byteValue();
169 public int calcIcmpv6Checksum(byte[] packet, Ipv6Header ip6Hdr) {
170 long checksum = getSummation(ip6Hdr.getSourceIpv6());
171 checksum += getSummation(ip6Hdr.getDestinationIpv6());
172 checksum = normalizeChecksum(checksum);
174 checksum += ip6Hdr.getIpv6Length();
175 checksum += ip6Hdr.getNextHeader();
177 int icmp6Offset = Ipv6Constants.ICMPV6_OFFSET;
178 long value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
180 checksum = normalizeChecksum(checksum);
183 //move to icmp6 payload skipping the checksum field
185 int length = packet.length - icmp6Offset;
187 value = (((packet[icmp6Offset] & 0xff) << 8) | (packet[icmp6Offset + 1] & 0xff));
189 checksum = normalizeChecksum(checksum);
195 checksum += packet[icmp6Offset];
196 checksum = normalizeChecksum(checksum);
199 int finalChecksum = (int)(~checksum & 0xffff);
200 return finalChecksum;
203 public boolean validateChecksum(byte[] packet, Ipv6Header ip6Hdr, int recvChecksum) {
204 int checksum = calcIcmpv6Checksum(packet, ip6Hdr);
206 if (checksum == recvChecksum) {
212 private long getSummation(Ipv6Address addr) {
215 baddr = InetAddress.getByName(addr.getValue()).getAddress();
216 } catch (UnknownHostException e) {
217 LOG.error("getSummation: Failed to deserialize address {}", addr.getValue(), e);
223 while (len < baddr.length) {
224 value = (((baddr[len] & 0xff) << 8) | (baddr[len + 1] & 0xff));
226 sum = normalizeChecksum(sum);
232 private long normalizeChecksum(long value) {
233 if ((value & 0xffff0000) > 0) {
234 value = (value & 0xffff);
240 public byte[] convertEthernetHeaderToByte(EthernetHeader ethPdu) {
241 byte[] data = new byte[16];
242 Arrays.fill(data, (byte)0);
244 ByteBuffer buf = ByteBuffer.wrap(data);
245 buf.put(bytesFromHexString(ethPdu.getDestinationMac().getValue().toString()));
246 buf.put(bytesFromHexString(ethPdu.getSourceMac().getValue().toString()));
247 buf.putShort((short)ethPdu.getEthertype().intValue());
251 public byte[] convertIpv6HeaderToByte(Ipv6Header ip6Pdu) {
252 byte[] data = new byte[128];
253 Arrays.fill(data, (byte)0);
255 ByteBuffer buf = ByteBuffer.wrap(data);
256 long flowLabel = (((long)(ip6Pdu.getVersion().shortValue() & 0x0f) << 28)
257 | (ip6Pdu.getFlowLabel().longValue() & 0x0fffffff));
258 buf.putInt((int)flowLabel);
259 buf.putShort((short)ip6Pdu.getIpv6Length().intValue());
260 buf.put((byte)ip6Pdu.getNextHeader().shortValue());
261 buf.put((byte)ip6Pdu.getHopLimit().shortValue());
263 byte[] baddr = InetAddress.getByName(ip6Pdu.getSourceIpv6().getValue()).getAddress();
265 baddr = InetAddress.getByName(ip6Pdu.getDestinationIpv6().getValue()).getAddress();
267 } catch (UnknownHostException e) {
268 LOG.error("convertIpv6HeaderToByte: Failed to serialize src, dest address", e);
273 public Ipv6Address getIpv6LinkLocalAddressFromMac(MacAddress mac) {
274 byte[] octets = bytesFromHexString(mac.getValue());
276 /* As per the RFC2373, steps involved to generate a LLA include
277 1. Convert the 48 bit MAC address to 64 bit value by inserting 0xFFFE
278 between OUI and NIC Specific part.
279 2. Invert the Universal/Local flag in the OUI portion of the address.
280 3. Use the prefix "FE80::/10" along with the above 64 bit Interface
281 identifier to generate the IPv6 LLA. */
283 StringBuffer interfaceID = new StringBuffer();
284 short u8byte = (short) (octets[0] & 0xff);
286 interfaceID.append(Integer.toHexString(0xFF & u8byte));
287 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[1]), 2, "0"));
288 interfaceID.append(":");
289 interfaceID.append(Integer.toHexString(0xFF & octets[2]));
290 interfaceID.append("ff:fe");
291 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[3]), 2, "0"));
292 interfaceID.append(":");
293 interfaceID.append(Integer.toHexString(0xFF & octets[4]));
294 interfaceID.append(StringUtils.leftPad(Integer.toHexString(0xFF & octets[5]), 2, "0"));
296 Ipv6Address ipv6LLA = new Ipv6Address("fe80:0:0:0:" + interfaceID.toString());
300 private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
301 List<MatchInfo> matches = new ArrayList<>();
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 matches.add(new MatchInfo(MatchFieldType.metadata,
309 new BigInteger[] { ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE}));
313 private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, String ndTarget) {
314 List<MatchInfo> matches = new ArrayList<>();
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 matches.add(new MatchInfo(MatchFieldType.metadata,
324 new BigInteger[] { ElanUtils.getElanMetadataLabel(elanTag), MetaDataUtil.METADATA_MASK_SERVICE}));
328 private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
329 return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
330 .append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
331 .append(elanTag).append(Ipv6Constants.FLOWID_SEPARATOR)
332 .append(flowType).toString();
335 public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId, Long elanTag, String ipv6Address,
336 IMdsalApiManager mdsalUtil,int addOrRemove) {
337 List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
338 List<InstructionInfo> instructions = new ArrayList<>();
339 List<ActionInfo> actionsInfos = new ArrayList<>();
340 actionsInfos.add(new ActionInfo(ActionType.punt_to_controller,
342 instructions.add(new InstructionInfo(InstructionType.apply_actions,
344 FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
345 getIPv6FlowRef(dpId, elanTag, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
346 0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
347 if (addOrRemove == Ipv6Constants.DEL_FLOW) {
348 LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
349 mdsalUtil.removeFlow(rsFlowEntity);
351 LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
352 mdsalUtil.installFlow(rsFlowEntity);
356 public void installIcmpv6RsPuntFlow(short tableId, BigInteger dpId, Long elanTag, IMdsalApiManager mdsalUtil,
358 if (dpId == null || dpId.equals(Ipv6Constants.INVALID_DPID)) {
361 List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
362 List<InstructionInfo> instructions = new ArrayList<>();
363 List<ActionInfo> actionsInfos = new ArrayList<>();
364 // Punt to controller
365 actionsInfos.add(new ActionInfo(ActionType.punt_to_controller,
367 instructions.add(new InstructionInfo(InstructionType.apply_actions,
369 FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
370 getIPv6FlowRef(dpId, elanTag, "IPv6RS"),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
371 NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
372 if (addOrRemove == Ipv6Constants.DEL_FLOW) {
373 LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
374 mdsalUtil.removeFlow(rsFlowEntity);
376 LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
377 mdsalUtil.installFlow(rsFlowEntity);
381 public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
382 BigInteger cookie, List<Instruction> instructions) {
383 StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
384 .setFlowPriority(flowPriority).setInstruction(instructions);
385 return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
386 .setServiceName(serviceName).setServicePriority(servicePriority)
387 .setServiceType(ServiceTypeFlowBased.class)
388 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
391 private InstanceIdentifier buildServiceId(String interfaceName,
393 return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
394 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
395 .child(BoundServices.class, new BoundServicesKey(priority)).build();
398 public void bindIpv6Service(DataBroker broker, String interfaceName, Long elanTag, short tableId) {
399 int instructionKey = 0;
400 List<Instruction> instructions = new ArrayList<>();
401 instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(ElanUtils.getElanMetadataLabel(elanTag),
402 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
403 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
404 short serviceIndex = ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME, NwConstants.IPV6_SERVICE_INDEX);
407 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
408 serviceIndex, Ipv6Constants.DEFAULT_FLOW_PRIORITY,
409 NwConstants.COOKIE_IPV6_TABLE, instructions);
410 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
411 buildServiceId(interfaceName, serviceIndex), serviceInfo);
414 public void unbindIpv6Service(DataBroker broker, String interfaceName) {
415 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
416 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
417 NwConstants.IPV6_SERVICE_INDEX)));
420 public static BigInteger getDataPathId(String dpId) {
423 dpid = new BigInteger(dpId.replaceAll(":", ""), 16).longValue();
425 return BigInteger.valueOf(dpid);