blob: 5f5166549aa1d8ad23c17eaff7e7046e0f966ea8 [file] [log] [blame]
package org.openflow.types;
import java.util.regex.Pattern;
import org.jboss.netty.buffer.ChannelBuffer;
import org.openflow.exceptions.OFParseError;
import org.openflow.exceptions.OFShortRead;
import org.openflow.exceptions.OFShortWrite;
import org.openflow.protocol.OFObject;
/**
* IPv6 address object. Instance controlled, immutable. Internal representation:
* two 64 bit longs (not that you'd have to know).
*
* @author Andreas Wundsam <andreas.wundsam@teleteach.de>
*/
public class IPv6 implements OFObject {
static final int LENGTH = 16;
private final long raw1;
private final long raw2;
private IPv6(final long raw1, final long raw2) {
this.raw1 = raw1;
this.raw2 = raw2;
}
public static IPv6 of(final byte[] address) {
if (address.length != LENGTH) {
throw new IllegalArgumentException(
"Invalid byte array length for IPv6 address: " + address);
}
long raw1 =
(address[0] & 0xFFL) << 56 | (address[1] & 0xFFL) << 48
| (address[2] & 0xFFL) << 40 | (address[3] & 0xFFL) << 32
| (address[4] & 0xFFL) << 24 | (address[5] & 0xFFL) << 16
| (address[6] & 0xFFL) << 8 | (address[7]);
long raw2 =
(address[8] & 0xFFL) << 56 | (address[9] & 0xFFL) << 48
| (address[10] & 0xFFL) << 40 | (address[11] & 0xFFL) << 32
| (address[12] & 0xFFL) << 24 | (address[13] & 0xFFL) << 16
| (address[14] & 0xFFL) << 8 | (address[15]);
return IPv6.of(raw1, raw2);
}
private static class IPv6Builder {
private long raw1, raw2;
public void setUnsignedShortWord(final int i, final int value) {
int shift = 48 - (i % 4) * 16;
if (value < 0 || value > 0xFFFF)
throw new IllegalArgumentException("16 bit word must be in [0, 0xFFFF]");
if (i >= 0 && i < 4)
raw1 = raw1 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
else if (i >= 4 && i < 8)
raw2 = raw2 & ~(0xFFFFL << shift) | (value & 0xFFFFL) << shift;
else
throw new IllegalArgumentException("16 bit word index must be in [0,7]");
}
public IPv6 getIPv6() {
return IPv6.of(raw1, raw2);
}
}
private final static Pattern colonPattern = Pattern.compile(":");
public static IPv6 of(final String string) {
IPv6Builder builder = new IPv6Builder();
String[] parts = colonPattern.split(string, -1);
int leftWord = 0;
int leftIndex = 0;
boolean hitZeroCompression = false;
for (leftIndex = 0; leftIndex < parts.length; leftIndex++) {
String part = parts[leftIndex];
if (part.length() == 0) {
// hit empty group of zero compression
hitZeroCompression = true;
break;
}
builder.setUnsignedShortWord(leftWord++, Integer.parseInt(part, 16));
}
if (hitZeroCompression) {
if (leftIndex == 0) {
// if colon is at the start, two columns must be at the start,
// move to the second empty group
leftIndex = 1;
if (parts.length < 2 || parts[1].length() > 0)
throw new IllegalArgumentException("Malformed IPv6 address: " + string);
}
int rightWord = 7;
int rightIndex;
for (rightIndex = parts.length - 1; rightIndex > leftIndex; rightIndex--) {
String part = parts[rightIndex];
if (part.length() == 0)
break;
builder.setUnsignedShortWord(rightWord--, Integer.parseInt(part, 16));
}
if (rightIndex == parts.length - 1) {
// if colon is at the end, two columns must be at the end, move
// to the second empty group
if (rightIndex < 1 || parts[rightIndex - 1].length() > 0)
throw new IllegalArgumentException("Malformed IPv6 address: " + string);
rightIndex--;
}
if (leftIndex != rightIndex)
throw new IllegalArgumentException("Malformed IPv6 address: " + string);
} else {
if (leftIndex != 8) {
throw new IllegalArgumentException("Malformed IPv6 address: " + string);
}
}
return builder.getIPv6();
}
public static IPv6 of(final long raw1, final long raw2) {
return new IPv6(raw1, raw2);
}
volatile byte[] bytesCache = null;
public byte[] getBytes() {
if (bytesCache == null) {
synchronized (this) {
if (bytesCache == null) {
bytesCache =
new byte[] { (byte) ((raw1 >> 56) & 0xFF),
(byte) ((raw1 >> 48) & 0xFF),
(byte) ((raw1 >> 40) & 0xFF),
(byte) ((raw1 >> 32) & 0xFF),
(byte) ((raw1 >> 24) & 0xFF),
(byte) ((raw1 >> 16) & 0xFF),
(byte) ((raw1 >> 8) & 0xFF),
(byte) ((raw1 >> 0) & 0xFF),
(byte) ((raw2 >> 56) & 0xFF),
(byte) ((raw2 >> 48) & 0xFF),
(byte) ((raw2 >> 40) & 0xFF),
(byte) ((raw2 >> 32) & 0xFF),
(byte) ((raw2 >> 24) & 0xFF),
(byte) ((raw2 >> 16) & 0xFF),
(byte) ((raw2 >> 8) & 0xFF),
(byte) ((raw2 >> 0) & 0xFF) };
}
}
}
return bytesCache;
}
@Override
public int getLength() {
return LENGTH;
}
public static IPv6 readFrom(final ChannelBuffer bb) throws OFParseError, OFShortRead {
return IPv6.of(bb.readLong(), bb.readLong());
}
@Override
public void writeTo(final ChannelBuffer bb) throws OFParseError, OFShortWrite {
bb.writeLong(raw1);
bb.writeLong(raw2);
}
@Override
public String toString() {
return toString(true, false);
}
public int getUnsignedShortWord(final int i) {
if (i >= 0 && i < 4)
return (int) ((raw1 >>> (48 - i * 16)) & 0xFFFF);
else if (i >= 4 && i < 8)
return (int) ((raw2 >>> (48 - (i - 4) * 16)) & 0xFFFF);
else
throw new IllegalArgumentException("16 bit word index must be in [0,7]");
}
/** get the index of the first word where to apply IPv6 zero compression */
public int getZeroCompressStart() {
int start = Integer.MAX_VALUE;
int maxLength = -1;
int candidateStart = -1;
for (int i = 0; i < 8; i++) {
if (candidateStart >= 0) {
// in a zero octect
if (getUnsignedShortWord(i) != 0) {
// end of this candidate word
int candidateLength = i - candidateStart;
if (candidateLength >= maxLength) {
start = candidateStart;
maxLength = candidateLength;
}
candidateStart = -1;
}
} else {
// not in a zero octect
if (getUnsignedShortWord(i) == 0) {
candidateStart = i;
}
}
}
if (candidateStart >= 0) {
int candidateLength = 8 - candidateStart;
if (candidateLength >= maxLength) {
start = candidateStart;
maxLength = candidateLength;
}
}
return start;
}
public String toString(final boolean zeroCompression, final boolean leadingZeros) {
StringBuilder res = new StringBuilder();
int compressionStart = zeroCompression ? getZeroCompressStart() : Integer.MAX_VALUE;
boolean inCompression = false;
boolean colonNeeded = false;
for (int i = 0; i < 8; i++) {
int word = getUnsignedShortWord(i);
if (word == 0) {
if (inCompression)
continue;
else if (i == compressionStart) {
res.append(':').append(':');
inCompression = true;
colonNeeded = false;
continue;
}
} else {
inCompression = false;
}
if (colonNeeded) {
res.append(':');
colonNeeded = false;
}
res.append(leadingZeros ? String.format("%04x", word) : Integer.toString(word,
16));
colonNeeded = true;
}
return res.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (raw1 ^ (raw1 >>> 32));
result = prime * result + (int) (raw2 ^ (raw2 >>> 32));
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IPv6 other = (IPv6) obj;
if (raw1 != other.raw1)
return false;
if (raw2 != other.raw2)
return false;
return true;
}
}