/* * Copyright (c) 2013 Pantheon Technologies s.r.o. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowjava.util; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.primitives.UnsignedBytes; import io.netty.buffer.ByteBuf; import io.netty.buffer.UnpooledByteBufAllocator; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.IetfYangUtil; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader; /** Class for common operations on ByteBuf * @author michal.polkorab * @author timotej.kubas */ public abstract class ByteBufUtils { public static final Splitter DOT_SPLITTER = Splitter.on('.'); public static final Splitter COLON_SPLITTER = Splitter.on(':'); private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); private static final Splitter HEXSTRING_SPLITTER = Splitter.onPattern("\\s+").omitEmptyStrings(); private static final Splitter HEXSTRING_NOSPACE_SPLITTER = Splitter.onPattern("(?<=\\G.{2})").omitEmptyStrings(); private ByteBufUtils() { //not called } /** * Converts ByteBuf into String * @param bb input ByteBuf * @return String */ public static String byteBufToHexString(final ByteBuf bb) { StringBuilder sb = new StringBuilder(); for (int i = bb.readerIndex(); i < (bb.readerIndex() + bb.readableBytes()); i++) { sb.append(String.format(" %02x", bb.getUnsignedByte(i))); } return sb.toString().trim(); } /** * Converts String into byte[] * @param hexSrc input String * @return byte[] filled with input data */ public static byte[] hexStringToBytes(final String hexSrc) { return hexStringToBytes(hexSrc, true); } /** * Converts String into byte[] * @param hexSrc input String * @param withSpaces if there are spaces in string * @return byte[] filled with input data */ public static byte[] hexStringToBytes(final String hexSrc, final boolean withSpaces) { final Splitter splitter = withSpaces ? HEXSTRING_SPLITTER : HEXSTRING_NOSPACE_SPLITTER; List<String> byteChips = Lists.newArrayList(splitter.split(hexSrc)); byte[] result = new byte[byteChips.size()]; int i = 0; for (String chip : byteChips) { result[i] = (byte) Short.parseShort(chip, 16); i++; } return result; } /** * Creates ByteBuf filled with specified data * @param hexSrc input String of bytes in hex format * @return ByteBuf with specified hexString converted */ public static ByteBuf hexStringToByteBuf(final String hexSrc) { ByteBuf out = UnpooledByteBufAllocator.DEFAULT.buffer(); hexStringToByteBuf(hexSrc, out); return out; } /** * Creates ByteBuf filled with specified data * @param hexSrc input String of bytes in hex format * @param out ByteBuf with specified hexString converted */ public static void hexStringToByteBuf(final String hexSrc, final ByteBuf out) { out.writeBytes(hexStringToBytes(hexSrc)); } /** * Fills specified ByteBuf with 0 (zeros) of desired length, used for padding * @param length * @param out ByteBuf to be padded * @deprecated Use {@link ByteBuf#writeZero(int)} directly. */ @Deprecated public static void padBuffer(final int length, final ByteBuf out) { out.writeZero(length); } /** * Create standard OF header * @param msgType message code * @param message POJO * @param out writing buffer * @param length ofheader length */ public static <E extends OfHeader> void writeOFHeader(final byte msgType, final E message, final ByteBuf out, final int length) { out.writeByte(message.getVersion()); out.writeByte(msgType); out.writeShort(length); out.writeInt(message.getXid().intValue()); } /** * Write length standard OF header * @param out writing buffer */ public static void updateOFHeaderLength(final ByteBuf out) { out.setShort(EncodeConstants.OFHEADER_LENGTH_INDEX, out.readableBytes()); } /** * Write length OF header * @param out writing buffer * @param index writing index */ public static void updateOFHeaderLength(final ByteBuf out, int index) { out.setShort(index + EncodeConstants.OFHEADER_LENGTH_INDEX, out.writerIndex() - index); } /** * Fills the bitmask from boolean map where key is bit position * @param booleanMap bit to boolean mapping * @return bit mask */ public static int fillBitMaskFromMap(final Map<Integer, Boolean> booleanMap) { int bitmask = 0; for (Entry<Integer, Boolean> iterator : booleanMap.entrySet()) { if (iterator.getValue() != null && iterator.getValue().booleanValue()) { bitmask |= 1 << iterator.getKey(); } } return bitmask; } /** * Fills the bitmask from a set of bit values, starting at specified offset. * * @param offset Bit offset to start at * @param values boolean bit values to fill * @return Filled-in bitmask */ public static int fillBitMask(final int offset, final boolean... values) { int bitmask = 0; int i = offset; for (boolean v : values) { if (v) { bitmask |= 1 << i; } ++i; } return bitmask; } /** * Fills the bitmask from boolean list where key is bit position * @param booleanList bit to boolean mapping * @return bit mask */ public static int[] fillBitMaskFromList(final List<Boolean> booleanList) { int[] bitmask; int index = 0; int arrayIndex = 0; if ((booleanList.size() % Integer.SIZE) != 0) { bitmask = new int[booleanList.size() / Integer.SIZE + 1]; } else { bitmask = new int[booleanList.size() / Integer.SIZE]; } for (Boolean currElement : booleanList) { if (currElement != null && currElement.booleanValue()) { bitmask[arrayIndex] |= 1 << index; } index++; arrayIndex = index / Integer.SIZE; } return bitmask; } /** * Converts byte array into String * @param array input byte array * @return String */ public static String bytesToHexString(final byte[] array) { StringBuilder sb = new StringBuilder(); for (byte element : array) { sb.append(String.format(" %02x", element)); } return sb.toString().trim(); } private static int hexValue(final char c) { if (c >= '0' && c <= '9') { return c - '0'; } if (c >= 'a' && c <= 'f') { return c - 'a' + 10; } if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } throw new IllegalArgumentException(String.format("Invalid character '%s' encountered", c)); } /** * Converts macAddress to byte array. * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}. * * @param macAddress * @return byte representation of mac address */ public static byte[] macAddressToBytes(final String macAddress) { final byte[] result = new byte[EncodeConstants.MAC_ADDRESS_LENGTH]; final char[] mac = macAddress.toCharArray(); try { int offset = 0; for (int i = 0; i < EncodeConstants.MAC_ADDRESS_LENGTH - 1; ++i) { if (mac[offset + EncodeConstants.SIZE_OF_BYTE_IN_BYTES] == ':') { result[i] = UnsignedBytes.checkedCast(hexValue(mac[offset])); offset++; } else { result[i] = UnsignedBytes.checkedCast( (hexValue(mac[offset]) << 4) | hexValue(mac[offset +1])); offset += 2; } Preconditions.checkArgument(mac[offset] == ':', "Invalid value: %s", macAddress); offset++; } if (offset == (mac.length - 1)) { result[EncodeConstants.MAC_ADDRESS_LENGTH - 1] = UnsignedBytes.checkedCast(hexValue(mac[offset])); } else { result[EncodeConstants.MAC_ADDRESS_LENGTH - 1] = UnsignedBytes.checkedCast(hexValue(mac[offset]) << 4 | hexValue(mac[offset +1])); offset++; } if (offset != (mac.length -1)) { throw new IllegalArgumentException("Incorrect MAC address length"); } } catch (Exception e) { throw new IllegalArgumentException("Unable to serialize MAC address for input: " + macAddress + ". \n" + e); } return result; } private static final void appendHexByte(final StringBuilder sb, final byte b) { final int v = UnsignedBytes.toInt(b); sb.append(HEX_CHARS[v >>> 4]); sb.append(HEX_CHARS[v & 15]); } private static void appendHexUnsignedShort(final StringBuilder sb, final int val) { sb.append(ByteBufUtils.HEX_CHARS[(val >>> 12) & 15]); sb.append(ByteBufUtils.HEX_CHARS[(val >>> 8) & 15]); sb.append(ByteBufUtils.HEX_CHARS[(val >>> 4) & 15]); sb.append(ByteBufUtils.HEX_CHARS[ val & 15]); } /** * Converts a MAC address represented in bytes to String. * See also {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}. * * @param address * @return String representation of a MAC address */ public static String macAddressToString(final byte[] address) { Preconditions.checkArgument(address.length == EncodeConstants.MAC_ADDRESS_LENGTH); final StringBuilder sb = new StringBuilder(17); appendHexByte(sb, address[0]); for (int i = 1; i < EncodeConstants.MAC_ADDRESS_LENGTH; i++) { sb.append(':'); appendHexByte(sb, address[i]); } return sb.toString(); } /** * Reads and parses null-terminated string from ByteBuf * @param rawMessage * @param length maximal length of String * @return String with name of port */ public static String decodeNullTerminatedString(final ByteBuf rawMessage, final int length) { byte[] name = new byte[length]; rawMessage.readBytes(name); return new String(name).trim(); } /** * Read an IPv4 address from a buffer and format it into dotted-quad string. * * @param buf Input buffer * @return Dotted-quad string */ public static String readIpv4Address(final ByteBuf buf) { final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV4_ADDRESS * 4 - 1); sb.append(buf.readUnsignedByte()); for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV4_ADDRESS; i++) { sb.append('.'); sb.append(buf.readUnsignedByte()); } return sb.toString(); } /** * Read an IPv6 address from a buffer and format it into a string of eight groups of four * hexadecimal digits separated by colons. * * @param buf Input buffer * @return IPv6 address in string format */ public static String readIpv6Address(final ByteBuf buf) { final StringBuilder sb = new StringBuilder(EncodeConstants.GROUPS_IN_IPV6_ADDRESS * 5 - 1); appendHexUnsignedShort(sb, buf.readUnsignedShort()); for (int i = 1; i < EncodeConstants.GROUPS_IN_IPV6_ADDRESS; i++) { sb.append(':'); appendHexUnsignedShort(sb, buf.readUnsignedShort()); } return sb.toString(); } public static Ipv4Address readIetfIpv4Address(final ByteBuf buf) { final byte[] tmp = new byte[4]; buf.readBytes(tmp); return IetfInetUtil.INSTANCE.ipv4AddressFor(tmp); } public static Ipv6Address readIetfIpv6Address(final ByteBuf buf) { final byte[] tmp = new byte[16]; buf.readBytes(tmp); return IetfInetUtil.INSTANCE.ipv6AddressFor(tmp); } public static MacAddress readIetfMacAddress(final ByteBuf buf) { final byte[] tmp = new byte[EncodeConstants.MAC_ADDRESS_LENGTH]; buf.readBytes(tmp); return IetfYangUtil.INSTANCE.macAddressFor(tmp); } public static byte[] serializeList(final List<Short> list) throws IOException{ ByteBuffer byteBuffer = ByteBuffer.allocate(list.size() * 2); for (Short aShort : list) { byteBuffer.putShort(aShort); } return byteBuffer.array(); } }