/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity / NetStack // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: IPv6.cs // using System; using System.Text; namespace System.Net.IP { /// /// IPv6 Address Structure. /// [ CLSCompliant(false) ] public struct IPv6 : IComparable { private uint u0, u1, u2, u3; /// /// The number of bytes in an IPv6 address. /// public const int Length = 16; /// /// The number of bits in an IPv6 address. /// public const int BitCount = 128; /// /// Constructor. /// /// The most significant 32-bits in /// host order. /// The next significant 32-bits in /// host order. /// The next significant 32-bits in /// host order. /// The least significant 32-bits in /// host order. public IPv6(uint q0, uint q1, uint q2, uint q3) { u0 = q0; u1 = q1; u2 = q2; u3 = q3; } #if HAVE_SYSTEM_NET_IPADDRESS /// /// Constructor /// /// An instance of the /// System.Net.IPAddress class. /// Thrown when /// argument is null. /// Thrown when /// AddressFamily of ipais other than /// InterNetwork6 public IPv6(IPAddress ipa) { if (ipa == null) { throw new ArgumentNullException(); } byte [] data = ipa.GetAddressBytes(); if (data.Length != Length) { throw new ArgumentException(); } u0 = (uint)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3])); u1 = (uint)((data[4] << 24) | (data[5] << 16) | (data[6] << 8) | (data[7])); u2 = (uint)((data[8] << 24) | (data[9] << 16) | (data[10] << 8) | (data[11])); u3 = (uint)((data[12] << 24) | (data[13] << 16) | (data[14] << 8) | (data[15])); } #endif // HAVE_SYSTEM_NET_IPADDRESS /// /// Provide a copy of the IPv6 address as an array of bytes. /// /// Array of bytes, ordered MSB to LSB. public byte[] GetAddressBytes() { return new byte[Length] { (byte)(u0 >> 24), (byte)(u0 >> 16), (byte)(u0 >> 8), (byte)(u0), (byte)(u1 >> 24), (byte)(u1 >> 16), (byte)(u1 >> 8), (byte)(u1), (byte)(u2 >> 24), (byte)(u2 >> 16), (byte)(u2 >> 8), (byte)(u2), (byte)(u3 >> 24), (byte)(u3 >> 16), (byte)(u3 >> 8), (byte)(u3) }; } /// /// Create an IPv6 address representing a netmask. /// /// /// An IPv6 instance. /// Thrown if maskLength is /// outside of the range [0,128]. public static IPv6 NetMask(int maskLength) { if ((maskLength > BitCount) || (maskLength < 0)) { throw new ArgumentException("Mask length greater than possible."); } return IPv6.AllOnes << (BitCount - maskLength); } /// /// Create an IPv6 address representing an IPv4 node that is only /// IPv4 capable. /// public static IPv6 CreateIPv4OnlyNodeAddress(IPv4 a) { return new IPv6(0, 0, 0xffff, (uint)a); } /// /// Create an IPv6 address representing an IPv4 node is IPv4 /// and IPv6 capable. /// public static IPv6 CreateIPv4NodeAddress(IPv4 a) { return new IPv6(0, 0, 0x0, (uint)a); } /// /// Create an IPv6 address from bytes in an array. The bytes /// are assumed to be in the order of MSB to LSB. /// /// Byte array to read address from. /// Offset in bytes of starting point. /// Thrown if the array /// is null. /// Thrown if there are less /// than 16 bytes from the offset to the end of the array. public static IPv6 ParseBytes(byte [] data, int offset) { if (data == null) { throw new ArgumentNullException(); } if (data.Length - offset < Length) { throw new ArgumentException("Byte array length not 16."); } return new IPv6((uint)((data[offset + 0] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | (data[offset + 3])), (uint)((data[offset + 4] << 24) | (data[offset + 5] << 16) | (data[offset + 6] << 8) | (data[offset + 7])), (uint)((data[offset + 8] << 24) | (data[offset + 9] << 16) | (data[offset + 10] << 8) | (data[offset + 11])), (uint)((data[offset + 12] << 24) | (data[offset + 13] << 16) | (data[offset + 14] << 8) | (data[offset + 15])) ); } /// /// Create an IPv6 address from bytes in an array. /// /// Thrown if the array /// is null. /// Thrown if there are less /// than 16 bytes in the array. public static IPv6 ParseBytes(byte [] data) { return ParseBytes(data, 0); } /// /// Converts characters in a range to parts of an IPv6 address. /// Input string. /// Start position in ipString /// End position in ipString /// The maximum number of 16bit address /// components to accept. /// Array to receive 16bit address components. /// /// private static int Parse(string! ipString, int start, int end, int maxValues, ref ushort[]! values) { const string hex = "00112233445566778899aAbBcCdDeEfF"; int valueIndex = 0; int digits = 0; for (int i = start; i < end; i++) { int n = hex.IndexOf(ipString[i]); digits ++; if (n >= 0) { if (valueIndex == maxValues || digits > 4) { // About to access value beyond specified range throw new FormatException(); } uint v = values[valueIndex]; v = v * 16 + (uint)(n / 2); if (v > 0xffffu) { throw new FormatException(); } values[valueIndex] = (ushort)v; } else if (ipString[i] == ':') { // Reached Separator or end valueIndex++; digits = 0; } else { // Invalid Character throw new FormatException(); } } return valueIndex + 1; } /// /// Converts an IP address string into an IPv6 instance. This string /// must be a pure IPv6 representation -- IPv4 representations are not /// supported. /// /// Thrown if /// ipString is null. /// Thrown if /// ipString is invalid. public static IPv6 Parse(string ipString) { if (ipString == null) throw new ArgumentNullException(); int zeroSep = ipString.IndexOf("::"); ushort[]! lhs = new ushort [8]; int nlhs; if (zeroSep >= 0) { nlhs = Parse(ipString, 0, zeroSep, 8, ref lhs); ushort[]! rhs = new ushort [8]; int nrhs = Parse(ipString, zeroSep + 2, ipString.Length, 8 - nlhs, ref rhs); if (nrhs + nlhs > 8) { throw new FormatException("Too many address components"); } for (int i = 0; i < nrhs; i++) { lhs[8 - nrhs + i] = rhs[i]; } } else { nlhs = Parse(ipString, 0, ipString.Length, 8, ref lhs); } return new IPv6( ((uint)lhs[0] << 16) | ((uint)lhs[1]), ((uint)lhs[2] << 16) | ((uint)lhs[3]), ((uint)lhs[4] << 16) | ((uint)lhs[5]), ((uint)lhs[6] << 16) | ((uint)lhs[7]) ); } /// /// Converts an IP address string into an IPv6 instance. /// /// Thrown if /// ipString is null. /// true on success, false on failure. public static bool Parse(string ipString, out IPv6 address) { try { address = Parse(ipString); } catch (FormatException) { address = IPv6.Zero; return false; } return true; } /// /// Writes network-order byte representation of IPv6 address /// into buffer at a specified offset. /// /// Thrown if buffer /// argument is null. /// Thrown if there is /// insufficient space between outputOffset and the end of /// buffer to write out the IP address. public int CopyOut(byte[] buffer, int outputOffset) { if (buffer == null) { throw new ArgumentNullException(); } if (buffer.Length - outputOffset < Length) { throw new ArgumentException("Byte array too short."); } buffer[outputOffset + 0] = (byte)(u0 >> 24); buffer[outputOffset + 1] = (byte)(u0 >> 16); buffer[outputOffset + 2] = (byte)(u0 >> 8); buffer[outputOffset + 3] = (byte)(u0); buffer[outputOffset + 4] = (byte)(u1 >> 24); buffer[outputOffset + 5] = (byte)(u1 >> 16); buffer[outputOffset + 6] = (byte)(u1 >> 8); buffer[outputOffset + 7] = (byte)(u1); buffer[outputOffset + 8] = (byte)(u2 >> 24); buffer[outputOffset + 9] = (byte)(u2 >> 16); buffer[outputOffset + 10] = (byte)(u2 >> 8); buffer[outputOffset + 11] = (byte)(u2); buffer[outputOffset + 12] = (byte)(u3 >> 24); buffer[outputOffset + 13] = (byte)(u3 >> 16); buffer[outputOffset + 14] = (byte)(u3 >> 8); buffer[outputOffset + 15] = (byte)(u3); return Length; } /// /// The less-than operator for two IPv6 addresses. /// /// /// /// True if the 128-bit number representing the lhs /// is less than rhs. public static bool operator < (IPv6 lhs, IPv6 rhs) { if (lhs.u0 != rhs.u0) return lhs.u0 < rhs.u0; if (lhs.u1 != rhs.u1) return lhs.u1 < rhs.u1; if (lhs.u2 != rhs.u2) return lhs.u2 < rhs.u2; return lhs.u3 < rhs.u3; } /// /// The less-than-or-equal-to operator for two IPv6 addresses. /// /// /// /// True if the 128-bit number representing the lhs /// is less than or equal to rhs. public static bool operator <= (IPv6 lhs, IPv6 rhs) { if (rhs.u0 > lhs.u0 || rhs.u1 > lhs.u1 || rhs.u2 > lhs.u2) return false; return lhs.u3 <= rhs.u3; } /// /// The greater-than operator for two IPv6 addresses. /// /// /// /// True if the 128-bit number representing the lhs /// is greater than rhs. public static bool operator > (IPv6 lhs, IPv6 rhs) { if (lhs.u0 != rhs.u0) return lhs.u0 > rhs.u0; if (lhs.u1 != rhs.u1) return lhs.u1 > rhs.u1; if (lhs.u2 != rhs.u2) return lhs.u2 > rhs.u2; return lhs.u3 > rhs.u3; } /// /// The greater-than-or-equal-to operator for two IPv6 addresses. /// /// /// /// True if the 128-bit number representing the lhs /// is greater than or equal to rhs. public static bool operator >= (IPv6 lhs, IPv6 rhs) { if (lhs.u0 < rhs.u0 || lhs.u1 < rhs.u1 || lhs.u2 < rhs.u2) return false; return lhs.u3 >= rhs.u3; } /// /// Equals operator. /// /// /// /// True if the addresses represented by lhs and /// rhs are the same. public static bool operator == (IPv6 lhs, IPv6 rhs) { return ((lhs.u0 == rhs.u0) && (lhs.u1 == rhs.u1) && (lhs.u2 == rhs.u2) && (lhs.u3 == rhs.u3)); } /// /// Not-equals operator. /// /// /// /// True if the addresses represented by lhs and /// rhs are different. public static bool operator != (IPv6 lhs, IPv6 rhs) { return ((lhs.u0 != rhs.u0) || (lhs.u1 != rhs.u1) || (lhs.u2 != rhs.u2) || (lhs.u3 != rhs.u3)); } /// /// Bit-wise AND operator. /// /// /// /// An IPv6 instance. public static IPv6 operator & (IPv6 lhs, IPv6 rhs) { return new IPv6(lhs.u0 & rhs.u0, lhs.u1 & rhs.u1, lhs.u2 & rhs.u2, lhs.u3 & rhs.u3); } /// /// Bit-wise OR operator. /// /// /// /// An IPv6 instance. public static IPv6 operator | (IPv6 lhs, IPv6 rhs) { return new IPv6(lhs.u0 | rhs.u0, lhs.u1 | rhs.u1, lhs.u2 | rhs.u2, lhs.u3 | rhs.u3); } /// /// Bit-wise XOR operator. /// /// /// /// An IPv6 instance. public static IPv6 operator ^ (IPv6 lhs, IPv6 rhs) { return new IPv6(lhs.u0 ^ rhs.u0, lhs.u1 ^ rhs.u1, lhs.u2 ^ rhs.u2, lhs.u3 ^ rhs.u3); } /// /// Bit-wise NOT operator. /// /// /// An IPv6 instance. public static IPv6 operator ~ (IPv6 ipv6) { return new IPv6(~ipv6.u0, ~ipv6.u1, ~ipv6.u2, ~ipv6.u3); } /// /// Increment IPv6 address. /// /// /// An IPv6 instance. public static IPv6 operator ++ (IPv6 ipv6) { uint u3 = ipv6.u3 + 1; if (u3 > ipv6.u3) return new IPv6(ipv6.u0, ipv6.u1, ipv6.u2, u3); uint u2 = ipv6.u2 + 1; if (u2 > ipv6.u2) return new IPv6(ipv6.u0, ipv6.u1, u2, u3); uint u1 = ipv6.u1 + 1; if (u1 > ipv6.u1) return new IPv6(ipv6.u0, u1, u2, u3); uint u0 = ipv6.u0 + 1; return new IPv6(u0, u1, u2, u3); } /// /// Decrement IPv6 address. /// /// /// An IPv6 instance. public static IPv6 operator -- (IPv6 ipv6) { uint u3 = ipv6.u3 - 1; if (u3 < ipv6.u3) return new IPv6(ipv6.u0, ipv6.u1, ipv6.u2, u3); uint u2 = ipv6.u2 - 1; if (u2 < ipv6.u2) return new IPv6(ipv6.u0, ipv6.u1, u2, u3); uint u1 = ipv6.u1 - 1; if (u1 < ipv6.u1) return new IPv6(ipv6.u0, u1, u2, u3); uint u0 = ipv6.u0 - 1; return new IPv6(u0, u1, u2, u3); } /// /// Right-shift operator. /// /// Address to be shifted. /// Number of bits to shift-by. /// An IPv6 address. public static IPv6 operator >> (IPv6 addr, int n) { switch (n >> 5) { case 3: addr.u3 = addr.u0; addr.u0 = addr.u1 = addr.u2 = 0; break; case 2: addr.u3 = addr.u1; addr.u2 = addr.u0; addr.u0 = addr.u1 = 0; break; case 1: addr.u3 = addr.u2; addr.u2 = addr.u1; addr.u1 = addr.u0; addr.u0 = 0; break; case 0: break; default: if (n < 0) return addr << (-n); return Zero; } n = n & 0x1f; if (n != 0) { int m = 32 - n; addr.u3 = (addr.u3 >> n) | (addr.u2 << m); addr.u2 = (addr.u2 >> n) | (addr.u1 << m); addr.u1 = (addr.u1 >> n) | (addr.u0 << m); addr.u0 = (addr.u0 >> n); } return new IPv6(addr.u0, addr.u1, addr.u2, addr.u3); } /// /// Left-shift operator. /// /// Address to be shifted. /// Number of bits to shift-by. /// An IPv6 address. public static IPv6 operator << (IPv6 addr, int n) { switch (n >> 5) { case 3: addr.u0 = addr.u3; addr.u1 = addr.u2 = addr.u3 = 0; break; case 2: addr.u0 = addr.u2; addr.u1 = addr.u3; addr.u2 = addr.u3 = 0; break; case 1: addr.u0 = addr.u1; addr.u1 = addr.u2; addr.u2 = addr.u3; addr.u3 = 0; break; case 0: break; default: if (n < 0) return addr >> (-n); return Zero; } n = n & 0x1f; if (n != 0) { int m = 32 - n; addr.u0 = (addr.u0 << n) | (addr.u1 >> m); addr.u1 = (addr.u1 << n) | (addr.u2 >> m); addr.u2 = (addr.u2 << n) | (addr.u3 >> m); addr.u3 = (addr.u3 << n); } return new IPv6(addr.u0, addr.u1, addr.u2, addr.u3); } /// /// Get a single bit from an IPv6 address. /// /// Index of bit (ordered from msb-to-lsb) /// /// Returns true if bit is set, false if /// bit is unset or is out /// of range. /// public bool GetBit(int bitIndex) { if (bitIndex < 0 || bitIndex >= BitCount) { return false; } int dwordIndex = bitIndex / 32; uint mask = 1u << (31 - (bitIndex & 31)); switch (dwordIndex) { case 0: return (u0 & mask) == mask; case 1: return (u1 & mask) == mask; case 2: return (u2 & mask) == mask; } return (u3 & mask) == mask; } /// /// Get the mask length from an IPv6 address representing a netmask. /// /// public static int GetMaskLength(IPv6 netmask) { int i = 0; while (netmask.GetBit(i) == true) { i++; } return i; } #if HAVE_SYSTEM_NET_IPADDRESS /// /// Cast IPv6 instance into a System.Net.IPAddress /// /// /// An IPAddress instance. public static explicit operator IPAddress(IPv6 ipv6) { return new IPAddress(ipv6.GetAddressBytes()); } #endif // HAVE_SYSTEM_NET_IPADDRESS /// /// Determines whether two Object instances are equal. /// /// Object to be compared to. /// True if o is an IPv6 address and numerically /// the same as instance. public override bool Equals(object o) { if (o is IPv6) { IPv6 ipo = (IPv6)o; return this == ipo; } return false; } /// /// Compute numeric hash of IPv6 instance. Value is /// suitable for use in hashing algorithms and data /// structures like a hash table. /// public override int GetHashCode() { return (int)(u0 ^ u1 ^ u2 ^ u3); } /// /// Determine if IPv6 address represents an IPv4 capable node. /// public bool RepresentsIPv4Address() { // RFC1884 Section 2.4.4 return (u0 == 0) && (u1 == 0) && ((u2 >> 16) == 0) && ((u2 & 0xffff) == 0xffff || ((u2 & 0xffff) == 0) && (u3 != 0)); } /// /// Determine if IPv6 address represents IPv4 only capable node. /// public bool RepresentsIPv4OnlyAddress() { // RFC1884 Section 2.4.4 return (u0 == 0) && (u1 == 0) && ((u2 >> 16) == 0) && ((u2 & 0xffff) == 0xffff); } /// /// Determine if IPv6 address represents a link-local address. /// public bool IsLinkLocalAddress() { return (u0 & 0xffc00000) == 0xfe800000; // RFC1884 Section 2.3 } /// /// Determine if IPv6 address represents a site-local address. /// public bool IsSiteLocalAddress() { return (u0 & 0xffc00000) == 0xfec00000; // RFC1884 Section 2.3 } /// /// Indicates whether instance represents a native IPv6 /// loopback address. /// /// True if instance represents a loopback /// address. public bool IsLoopback() { return (this == IPv6.Loopback); } /// /// Determine if IPv6 address represents a native IPv6 /// multicast address. /// public bool IsMulticast() { return ((u0 & 0xff000000) == 0xff000000); } /// /// Extract embedded IPv4 address. /// /// Returns IPv4 address on success and IPv4.Any /// if address does not represent an IPv4 address. public IPv4 GetIPv4Address() { if (u0 == 0 && u1 == 0 && (u2 == 0 || u2 == 0xffff)) { return new IPv4(u3); } return IPv4.Any; } private string! NativeStringRepresentation() { // Copy address into array of ushorts to make it easy to find // region a region of zeroes per RFC 1884. We only find the // first region rather than attempt to find the longest. ushort [] words = new ushort [8] { (ushort)(u0 >> 16), (ushort)(u0 & 0xffffU), (ushort)(u1 >> 16), (ushort)(u1 & 0xffffU), (ushort)(u2 >> 16), (ushort)(u2 & 0xffffU), (ushort)(u3 >> 16), (ushort)(u3 & 0xffffU) }; // Find start of the zero block int zStart; for (zStart = 0; zStart < words.Length; zStart++) { if (words[zStart] == 0) break; } // Find the end of the zero block int zEnd; for (zEnd = zStart + 1; zEnd < words.Length; zEnd++) { if (words[zEnd] != 0) break; } // Max size is 8 * 4 hexdigits + 7 * 1 digit separator StringBuilder sb = new StringBuilder(null, 8 * 4 + 7 * 1); for (int i = 0; i < zStart; i++) { if (i > 0) sb.AppendFormat(":{0:x}", words[i]); else sb.AppendFormat("{0:x}", words[i]); } if (zStart == words.Length) return sb.ToString(); sb.Append("::"); for (int i = zEnd; i < words.Length; i++) { if (i > zEnd) sb.AppendFormat(":{0:x}", words[i]); else sb.AppendFormat("{0:x}", words[i]); } return sb.ToString(); } /// /// Returns a string representing IPv6 instance. /// public override string! ToString() { #if NOTYET if (RepresentsIPv4OnlyAddress() == true) { return String.Format("::ffff:{0}.{1}.{2}.{3}", u3 >> 24, (u3 >> 16) & 0xff, (u3 >> 8) & 0xff, u3 & 0xff); } else if (RepresentsIPv4Address() == true) { return String.Format("::{0}.{1}.{2}.{3}", u3 >> 24, (u3 >> 16) & 0xff, (u3 >> 8) & 0xff, u3 & 0xff); } #endif // NOTYET return NativeStringRepresentation(); } public int CompareTo(object other) { if (other == null) return 1; if (other is IPv6) { IPv6 value = (IPv6) other; if (this < value) return -1; if (this > value) return +1; return 0; } throw new ArgumentException ("Arg_MustBeIPv6"); } /// /// IPv6 address representing unspecified address. /// public static readonly IPv6 Any = new IPv6(0U, 0U, 0U, 0U); /// /// IPv6 address with all bits set to zero. /// public static readonly IPv6 Zero = new IPv6(0U, 0U, 0U, 0U); /// /// IPv6 loopback address. /// public static readonly IPv6 Loopback = new IPv6(0U, 0U, 0U, 1U); /// /// IPv6 address with all bits set to one. /// public static readonly IPv6 AllOnes = new IPv6(~0U, ~0U, ~0U, ~0U); } } // namespace System.Net.IP