Ayaka Koshibe

netmask support in IpAddress

Change-Id: Ie5f4276a1fa1cdd56bebbd3cd1ee74ecacdab598
......@@ -5,7 +5,7 @@ import java.util.Arrays;
/**
* A class representing an IPv4 address.
*/
public class IpAddress {
public final class IpAddress {
//IP Versions
public enum Version { INET, INET6 };
......@@ -14,13 +14,30 @@ public class IpAddress {
public static final int INET_LEN = 4;
public static final int INET6_LEN = 16;
//maximum CIDR value
public static final int MAX_INET_MASK = 32;
public static final int DEFAULT_MASK = 0;
/**
* Default value indicating an unspecified address.
*/
public static final byte [] ANY = new byte [] {0, 0, 0, 0};
protected Version version;
//does it make more sense to have a integral address?
protected byte[] octets;
protected int netmask;
protected IpAddress(Version ver, byte[] octets) {
private IpAddress(Version ver, byte[] octets, int netmask) {
this.version = ver;
this.octets = Arrays.copyOf(octets, INET_LEN);
this.netmask = netmask;
}
private IpAddress(Version ver, byte[] octets) {
this.version = ver;
this.octets = Arrays.copyOf(octets, INET_LEN);
this.netmask = DEFAULT_MASK;
}
/**
......@@ -34,38 +51,87 @@ public class IpAddress {
}
/**
* Converts an integer into an IPv4 address.
* Converts a byte array into an IP address.
*
* @param address an integer representing an IP value
* @param address a byte array
* @param netmask the CIDR value subnet mask
* @return an IP address
*/
public static IpAddress valueOf(int address) {
public static IpAddress valueOf(byte [] address, int netmask) {
return new IpAddress(Version.INET, address, netmask);
}
/**
* Helper to convert an integer into a byte array.
*
* @param address the integer to convert
* @return a byte array
*/
private static byte [] bytes(int address) {
byte [] bytes = new byte [INET_LEN];
for (int i = 0; i < INET_LEN; i++) {
bytes[i] = (byte) ((address >> (INET_LEN - (i + 1)) * 8) & 0xff);
}
return new IpAddress(Version.INET, bytes);
return bytes;
}
/**
* Converts an integer into an IPv4 address.
*
* @param address an integer representing an IP value
* @return an IP address
*/
public static IpAddress valueOf(int address) {
return new IpAddress(Version.INET, bytes(address));
}
/**
* Converts an integer into an IPv4 address.
*
* @param address an integer representing an IP value
* @param netmask the CIDR value subnet mask
* @return an IP address
*/
public static IpAddress valueOf(int address, int netmask) {
return new IpAddress(Version.INET, bytes(address), netmask);
}
/**
* Converts a string in dotted-decimal notation (x.x.x.x) into
* an IPv4 address.
* Converts a dotted-decimal string (x.x.x.x) into an IPv4 address. The
* string can also be in CIDR (slash) notation.
*
* @param address a string representing an IP address, e.g. "10.0.0.1"
* @param address a IP address in string form, e.g. "10.0.0.1", "10.0.0.1/24"
* @return an IP address
*/
public static IpAddress valueOf(String address) {
final String [] parts = address.split("\\.");
if (parts.length != INET_LEN) {
final String [] parts = address.split("\\/");
if (parts.length > 2) {
throw new IllegalArgumentException("Malformed IP address string; "
+ "Addres must take form \"x.x.x.x\" or \"x.x.x.x/y\"");
}
int mask = DEFAULT_MASK;
if (parts.length == 2) {
mask = Integer.valueOf(parts[1]);
if (mask > MAX_INET_MASK) {
throw new IllegalArgumentException(
"Value of subnet mask cannot exceed "
+ MAX_INET_MASK);
}
}
final String [] net = parts[0].split("\\.");
if (net.length != INET_LEN) {
throw new IllegalArgumentException("Malformed IP address string; "
+ "Addres must have four decimal values separated by dots (.)");
}
final byte [] bytes = new byte[INET_LEN];
for (int i = 0; i < INET_LEN; i++) {
bytes[i] = Byte.parseByte(parts[i], 10);
bytes[i] = (byte) Short.parseShort(net[i], 10);
}
return new IpAddress(Version.INET, bytes);
return new IpAddress(Version.INET, bytes, mask);
}
/**
......@@ -99,34 +165,122 @@ public class IpAddress {
return address;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
for (final byte b : this.octets) {
if (builder.length() > 0) {
builder.append(".");
/**
* Helper for computing the mask value from CIDR.
*
* @return an integer bitmask
*/
private int mask() {
int shift = MAX_INET_MASK - this.netmask;
return ((Integer.MAX_VALUE >>> (shift - 1)) << shift);
}
builder.append(String.format("%d", b));
/**
* Returns the subnet mask in IpAddress form. The netmask value for
* the returned IpAddress is 0, as the address itself is a mask.
*
* @return the subnet mask
*/
public IpAddress netmask() {
return new IpAddress(Version.INET, bytes(mask()));
}
return builder.toString();
/**
* Returns the network portion of this address as an IpAddress.
* The netmask of the returned IpAddress is the current mask. If this
* address doesn't have a mask, this returns an all-0 IpAddress.
*
* @return the network address or null
*/
public IpAddress network() {
if (netmask == DEFAULT_MASK) {
return new IpAddress(version, ANY, DEFAULT_MASK);
}
byte [] net = new byte [4];
byte [] mask = bytes(mask());
for (int i = 0; i < INET_LEN; i++) {
net[i] = (byte) (octets[i] & mask[i]);
}
return new IpAddress(version, net, netmask);
}
/**
* Returns the host portion of the IPAddress, as an IPAddress.
* The netmask of the returned IpAddress is the current mask. If this
* address doesn't have a mask, this returns a copy of the current
* address.
*
* @return the host address
*/
public IpAddress host() {
if (netmask == DEFAULT_MASK) {
new IpAddress(version, octets, netmask);
}
byte [] host = new byte [INET_LEN];
byte [] mask = bytes(mask());
for (int i = 0; i < INET_LEN; i++) {
host[i] = (byte) (octets[i] & ~mask[i]);
}
return new IpAddress(version, host, netmask);
}
@Override
public int hashCode() {
return Arrays.hashCode(octets);
final int prime = 31;
int result = 1;
result = prime * result + netmask;
result = prime * result + Arrays.hashCode(octets);
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof IpAddress) {
IpAddress other = (IpAddress) obj;
if (this.version.equals(other.version)
&& (Arrays.equals(this.octets, other.octets))) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
IpAddress other = (IpAddress) obj;
if (netmask != other.netmask) {
return false;
}
if (!Arrays.equals(octets, other.octets)) {
return false;
}
if (version != other.version) {
return false;
}
return true;
}
@Override
/*
* (non-Javadoc)
* format is "x.x.x.x" for non-masked (netmask 0) addresses,
* and "x.x.x.x/y" for masked addresses.
*
* @see java.lang.Object#toString()
*/
public String toString() {
final StringBuilder builder = new StringBuilder();
for (final byte b : this.octets) {
if (builder.length() > 0) {
builder.append(".");
}
builder.append(String.format("%d", b & 0xff));
}
if (netmask != DEFAULT_MASK) {
builder.append("/");
builder.append(String.format("%d", netmask));
}
return builder.toString();
}
}
......
package org.onlab.packet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
......@@ -11,33 +12,65 @@ import com.google.common.testing.EqualsTester;
public class IPAddressTest {
private static final byte [] BYTES1 = new byte [] {0x0, 0x0, 0x0, 0xa};
private static final byte [] BYTES2 = new byte [] {0x0, 0x0, 0x0, 0xb};
private static final int INTVAL1 = 10;
private static final int INTVAL2 = 12;
private static final String STRVAL = "0.0.0.11";
private static final byte [] BYTES1 = new byte [] {0xa, 0x0, 0x0, 0xa};
private static final byte [] BYTES2 = new byte [] {0xa, 0x0, 0x0, 0xb};
private static final int INTVAL1 = 167772170;
private static final int INTVAL2 = 167772171;
private static final String STRVAL = "10.0.0.12";
private static final int MASK = 16;
@Test
public void testEquality() {
IpAddress ip1 = IpAddress.valueOf(BYTES1);
IpAddress ip2 = IpAddress.valueOf(BYTES2);
IpAddress ip3 = IpAddress.valueOf(INTVAL1);
IpAddress ip2 = IpAddress.valueOf(INTVAL1);
IpAddress ip3 = IpAddress.valueOf(BYTES2);
IpAddress ip4 = IpAddress.valueOf(INTVAL2);
IpAddress ip5 = IpAddress.valueOf(STRVAL);
new EqualsTester().addEqualityGroup(ip1, ip3)
.addEqualityGroup(ip2, ip5)
.addEqualityGroup(ip4)
new EqualsTester().addEqualityGroup(ip1, ip2)
.addEqualityGroup(ip3, ip4)
.addEqualityGroup(ip5)
.testEquals();
// string conversions
IpAddress ip6 = IpAddress.valueOf(BYTES1, MASK);
IpAddress ip7 = IpAddress.valueOf("10.0.0.10/16");
IpAddress ip8 = IpAddress.valueOf(new byte [] {0xa, 0x0, 0x0, 0xc});
assertEquals("incorrect address conversion", ip6, ip7);
assertEquals("incorrect address conversion", ip5, ip8);
}
@Test
public void basics() {
IpAddress ip4 = IpAddress.valueOf(BYTES1);
assertEquals("incorrect IP Version", Version.INET, ip4.version());
assertEquals("faulty toOctets()", Arrays.equals(
new byte [] {0x0, 0x0, 0x0, 0xa}, ip4.toOctets()), true);
assertEquals("faulty toInt()", INTVAL1, ip4.toInt());
assertEquals("faulty toString()", "0.0.0.10", ip4.toString());
IpAddress ip1 = IpAddress.valueOf(BYTES1, MASK);
final byte [] bytes = new byte [] {0xa, 0x0, 0x0, 0xa};
//check fields
assertEquals("incorrect IP Version", Version.INET, ip1.version());
assertEquals("incorrect netmask", 16, ip1.netmask);
assertTrue("faulty toOctets()", Arrays.equals(bytes, ip1.toOctets()));
assertEquals("faulty toInt()", INTVAL1, ip1.toInt());
assertEquals("faulty toString()", "10.0.0.10/16", ip1.toString());
}
@Test
public void netmasks() {
// masked
IpAddress ip1 = IpAddress.valueOf(BYTES1, MASK);
IpAddress host = IpAddress.valueOf("0.0.0.10/16");
IpAddress network = IpAddress.valueOf("10.0.0.0/16");
assertEquals("incorrect host address", host, ip1.host());
assertEquals("incorrect network address", network, ip1.network());
assertEquals("incorrect netmask", "255.255.0.0", ip1.netmask().toString());
//unmasked
IpAddress ip2 = IpAddress.valueOf(BYTES1);
IpAddress umhost = IpAddress.valueOf("10.0.0.10/0");
IpAddress umnet = IpAddress.valueOf("0.0.0.0/0");
assertEquals("incorrect host address", umhost, ip2.host());
assertEquals("incorrect host address", umnet, ip2.network());
assertTrue("incorrect netmask",
Arrays.equals(IpAddress.ANY, ip2.netmask().toOctets()));
}
}
......