Currently I code client-server junk and deal a lot with C++ structs passed over network.
I know about ways provided here Reading a C/C++ data structure in C# from a byte array, but they all about making a copy.
I want to have something like that:
struct/*or class*/ SomeStruct
{
public uint F1;
public uint F2;
public uint F3;
}
Later in my code I want to have something like that:
byte[] Data; //16 bytes that I got from network
SomeStruct PartOfDataAsSomeStruct { get { return /*make SomeStruct instance based on this.Data starting from index 4, without copying it. So when I do PartOfDataAsSomeStruct.F1 = 132465; it also changes bytes 4, 5, 6 and 7 in this.Data.*/; } }
If this is possible, please, tell how?
Like so?
byte[] data = new byte[16];
// 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct typed = ref Unsafe.As<byte, SomeStruct>(ref data[4]);
typed.F1 = 42;
typed.F2 = 3;
typed.F3 = 9;
// 00-00-00-00-2A-00-00-00-03-00-00-00-09-00-00-00
Console.WriteLine(BitConverter.ToString(data));
This coerces the data from the middle of the byte-array using a ref-local that is an "interior managed pointer" to the data. Zero copies.
If you need multiple items (like how a vector would work), you can do the same thing with spans and MemoryMarshal.Cast
Note that it uses CPU-endian rules for the elements - little endian in my case.
For spans:
byte[] data = new byte[256];
// create a span of some of it
var span = new Span<byte>(data, 4, 128);
// now coerce the span
var typed = MemoryMarshal.Cast<byte, SomeStruct>(span);
Console.WriteLine(typed.Length); // 10 of them fit
typed[3].F1 = 3; // etc
Thank you for the correction, Marc Gravell. And thank you for the example.
Here is a way using Class and Bitwise Operators, without pointers, to do the samething:
class SomeClass
{
public byte[] Data;
public SomeClass()
{
Data = new byte[16];
}
public uint F1
{
get
{
uint ret = (uint)(Data[4] << 24 | Data[5] << 16 | Data[6] << 8 | Data[7]);
return ret;
}
set
{
Data[4] = (byte)(value >> 24);
Data[5] = (byte)(value >> 16);
Data[6] = (byte)(value >> 8);
Data[7] = (byte)value;
}
}
}
Testing:
SomeClass sc = new SomeClass();
sc.F1 = 0b_00000001_00000010_00000011_00000100;
Console.WriteLine(sc.Data[3].ToString() + " " + sc.Data[4].ToString() + " " + sc.Data[5].ToString() + " " + sc.Data[6].ToString());
Console.WriteLine(sc.F1.ToString());
//Output:
//1 2 3 4
//16909060
I have Java code example, of how verification code should be computed. And I have to convert Java code to C#.
First of all, code is computed as:
integer(SHA256(hash)[-2: -1]) mod 10000
Where we take SHA256 result, extract 2 rightmost bytes from it, interpret them as big-endian unsigned integer and take the last 4 digits in decimal for display.
Java code:
public static String calculate(byte[] documentHash) {
byte[] digest = DigestCalculator.calculateDigest(documentHash, HashType.SHA256);
ByteBuffer byteBuffer = ByteBuffer.wrap(digest);
int shortBytes = Short.SIZE / Byte.SIZE; // Short.BYTES in java 8
int rightMostBytesIndex = byteBuffer.limit() - shortBytes;
short twoRightmostBytes = byteBuffer.getShort(rightMostBytesIndex);
int positiveInteger = ((int) twoRightmostBytes) & 0xffff;
String code = String.valueOf(positiveInteger);
String paddedCode = "0000" + code;
return paddedCode.substring(code.length());
}
public static byte[] calculateDigest(byte[] dataToDigest, HashType hashType) {
String algorithmName = hashType.getAlgorithmName();
return DigestUtils.getDigest(algorithmName).digest(dataToDigest);
}
So int C# from Base64 string:
2afAxT+nH5qNYrfM+D7F6cKAaCKLLA23pj8ro3SksqwsdwmC3xTndKJotewzu7HlDy/DiqgkR+HXBiA0sW1x0Q==
should compute code equal to: 3676
Any ideas how to implement this?
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetCode("2afAxT+nH5qNYrfM+D7F6cKAaCKLLA23pj8ro3SksqwsdwmC3xTndKJotewzu7HlDy/DiqgkR+HXBiA0sW1x0Q=="));
}
public static string GetCode(string str)
{
var sha = System.Security.Cryptography.SHA256.Create();
var hash = sha.ComputeHash(Convert.FromBase64String(str));
var last2 = hash[^2..];
var intVal = ((int) last2[0]) * 0x0100 + ((int) last2[1]);
var digits = intVal % 10000;
return $"{digits:0000}";
}
}
I am trying to program a PLC address generator. However there I need to make bit wise addition to find the next available address.
Meaning if I start with the adress 0.0 and add 2 bit then the next free adress would be 0.3. It goes up until 0.7 then then next adress is 1.0 up to 1.7 then 2.0 and so on.
Depending on what datatype I add to the addition the next free adress should be calculated.
For example a bool is one bit. 0.1 -> 0.2 -> 0.3 and so on
A Byte has 8 bits if I add a byte and the last free adress was 0.4 the next free address should be 2.0.
A Word has 16 Bits so 0.0 -> 2.0 -> 4.0 and so on.
A Double Word has 32 Bits so 0.0 -> 4.0 -> 8.0 and so on.
I am looking for an implementation in c# where I can add the different types as input and it adds it and gives me corresponding address and stores then next free address internal for the next operation.
For example:
Type Startaddress
1 Bool 0.0 (->0.1)
2 Bool 0.1 (->0.2)
3 Byte 1.0 (->1.7) as 8 bits are required
4 Bool 2.0 (->2.1)
5 Word 3.0 (->4.7) as 16 bits are required
6 Double Word 5.0 (->8.7) as 32 bits are required
Any idea how I could implement that apart from a lot of if else and loop? I am looking for an elegant overloaded operator approach.
The only "trick" to your problem is the notation of .0-.7 for bit addresses and the C# types don't match up exactly with the types in your specification.
The main class I show here stores the address as bit offset internally, and provides the integer and decimal fraction through the fAddress() method.
Your example shows alignment on byte boundaries, but doesn't align words or double words--so that's what I implemented. Comments show how to do that differently if the PLC cares.
You'll need to add the code to store values at the byte.bit type addresses.
using System;
namespace PLCAddress
{
class Program
{
static void Main(string[] args)
{
PLCAddress a = new PLCAddress();
float address;
bool boolA = true;
byte byteA = 7;
ushort wordA = 65535;
uint dblwordA = 4294967295;
address = a.Store(boolA);
Console.WriteLine(address.ToString());
address = a.Store(boolA);
Console.WriteLine(address.ToString());
address = a.Store(byteA);
Console.WriteLine(address.ToString());
address = a.Store(boolA);
Console.WriteLine(address.ToString());
address = a.Store(wordA);
Console.WriteLine(address.ToString());
address = a.Store(dblwordA);
Console.WriteLine(address.ToString());
}
}
public class PLCAddress
{
protected uint _address;
public PLCAddress()
{
_address = 0;
}
public float Store(bool b)
{
float rv = fAddress();
_address += 1;
return rv;
}
public float Store(byte b)
{
float rv = fAddress(8);
_address += 8;
return rv;
}
public float Store(ushort b)
{
float rv = fAddress(8); // use fAddress(16) if words need to be on word boundaries
_address += 16;
return rv;
}
public float Store(uint b)
{
float rv = fAddress(8); // use fAddress(32) if double words need to be on double word boundaries
_address += 32;
return rv;
}
protected float fAddress()
{
return (float)Whole + (float)Fraction / 10;
}
protected float fAddress(uint alignment)
{
uint roundup = alignment - 1;
uint mask = ~roundup;
uint AlignedAddress = _address + roundup;
AlignedAddress = AlignedAddress & mask;
_address = AlignedAddress;
return fAddress();
}
protected uint Whole
{
get { return _address / 8; }
}
protected uint Fraction
{
get { return _address % 8; }
}
}
}
You can store these addresses in an int - the lower part in the first 3 bits, the rest in the rest of the bits and get the address from there. This allows you to do normal arithmetic on these addresses and numbers. If the address is in a string you can do something like this:
public static int ToIntAddress(this string str)
{
var values = str.Split('.');
int lower = int.Parse(values[1]);
int higher = int.Parse(values[0]) << 3;
return lower + higher;
}
public static string ToAddress(this int address) => $"{address >> 3}.{address & 0b0111}";
("3.0".ToIntAddress() + 15).ToAddress() // "4.7"
("5.0".ToIntAddress() + 31).ToAddress() // "8.7"
("0.4".ToIntAddress() + 7).ToAddress() // "1.3"
I personally prefer an object oriented approach:
public class MemoryManager
{
private int _dataSize = 0;
public enum DataTypes
{
Bool = 1,
Byte = 8,
Word = 16,
DWord = 32
}
public MemoryLocation Add(DataTypes type)
{
var address = GetCurrentAddress();
_dataSize += (int)type;
return address;
}
private MemoryLocation GetCurrentAddress()
{
int currentByteLocation = _dataSize / 8;
int currentBitLocation = _dataSize % 8;
return new MemoryLocation(currentByteLocation, currentBitLocation);
}
}
public class MemoryLocation
{
public MemoryLocation(int byteLocation, int bitIndex)
{
ByteLocation = byteLocation;
BitIndex = bitIndex;
}
public int ByteLocation { get; private set; }
public int BitIndex { get; private set; }
public override string ToString()
{
return string.Format("[{0},{1}]", ByteLocation, BitIndex);
}
}
I whipped this out real quick but you can use other more streamlined methods to generate the next address.
You can use this:
public int BitWiseAdd()
{
int FirstNumber = 50;
int SecondNumber = 60;
while (SecondNumber !=0)
{
int carry = FirstNumber & SecondNumber;
FirstNumber = FirstNumber ^ SecondNumber;
SecondNumber = carry << 1;
}
return FirstNumber;
}
I am trying to figure out how to increment a starting ip address, and increment it by an offset that I specify. I have attempted to do this, but I am doing something wrong because I am getting IPs that are all over the place, not even in the same network range.
What I am currently doing is taking my starting ip and ending ip, getting the total amount of addresses then incrementing the total ips by an offset then attempting to actually increment the IP.
I am incrementing to the total ips by an offset so I know how many to increment the ip. (I am completing different tasks per offset.) Whatever the loop has incremented "t" to that is how many I increment IPs. Now that I have given the rundown, my issue only seems to be with actually incrementing ips, can anyone help me out in this situation. Thanks.
string from = txtStart.Text, to = txtEnd.Text;
uint current = from.ToUInt(), last = to.ToUInt();
ulong total = last - current;
int offset = 3; //This is an example number, it actually could be anything.
while (current <= last)
{
for (int t = 0; t < total; t += offset)
{
uint ut = Convert.ToUInt32(t);
current = current + ut;
var ip = current.ToIPAddress();
}
}
Here is the extension class I am using. They work fine.
public static class Extensions
{
public static uint ToUInt(this string ipAddress)
{
var ip = IPAddress.Parse(ipAddress);
var bytes = ip.GetAddressBytes();
Array.Reverse(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
public static string ToString(this uint ipInt)
{
return ToIPAddress(ipInt).ToString();
}
public static IPAddress ToIPAddress(this uint ipInt)
{
var bytes = BitConverter.GetBytes(ipInt);
Array.Reverse(bytes);
return new IPAddress(bytes);
}
}
[TestFixture]
public class GetNextIpAddressTest
{
[Test]
public void ShouldGetNextIp()
{
Assert.AreEqual("0.0.0.1", GetNextIpAddress("0.0.0.0", 1));
Assert.AreEqual("0.0.1.0", GetNextIpAddress("0.0.0.255", 1));
Assert.AreEqual("0.0.0.11", GetNextIpAddress("0.0.0.1", 10));
Assert.AreEqual("123.14.1.101", GetNextIpAddress("123.14.1.100", 1));
Assert.AreEqual("0.0.0.0", GetNextIpAddress("255.255.255.255", 1));
}
private static string GetNextIpAddress(string ipAddress, uint increment)
{
byte[] addressBytes = IPAddress.Parse(ipAddress).GetAddressBytes().Reverse().ToArray();
uint ipAsUint = BitConverter.ToUInt32(addressBytes, 0);
var nextAddress = BitConverter.GetBytes(ipAsUint + increment);
return String.Join(".", nextAddress.Reverse());
}
}
An IPv4 address is basically a 32 bit Integer. Therefore you can just parse the substrings from e.g. 192.168.0.1 and convert each byte to an integer number:
uint byte1 = Converter.ToUint32("192");
and so on ..
Then you could just "OR" or "ADD" them together like this:
uint IP = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
and increment that integer with step_size as needed. Here is an example:
using System.IO;
using System;
class Program
{
static void Main()
{
String ipString = "192.168.0.1";
String[] ipBytes = ipString.Split('.');
uint byte1 = Convert.ToUInt32(ipBytes[0]);
uint byte2 = Convert.ToUInt32(ipBytes[1]);
uint byte3 = Convert.ToUInt32(ipBytes[2]);
uint byte4 = Convert.ToUInt32(ipBytes[3]);
uint IP = (byte1 << 24)
| (byte2 << 16)
| (byte3 << 8)
| byte4 ;
uint step_size = 90000000;
while( IP != 0xFFFFFFFF ) {
Console.WriteLine(
((IP >> 24) & 0xFF) + "." +
((IP >> 16) & 0xFF) + "." +
((IP >> 8 ) & 0xFF) + "." +
( IP & 0xFF)
);
// if (0xFFFFFFFF - IP) < step_size then we can't
// add step_size to IP due to integer overlow
// which means that we have generated all IPs and
// there isn't any left that equals IP + step_size
if( (0xFFFFFFFF - IP) < step_size ) {
break;
}
IP += step_size; // next ip address
}
}
}
Output
192.168.0.1
198.5.74.129
203.98.149.1
208.191.223.129
214.29.42.1
219.122.116.129
224.215.191.1
230.53.9.129
235.146.84.1
240.239.158.129
246.76.233.1
251.170.51.129
The following is a class I use for working with IP addresses which includes the ability to increment an IP address as well as to build a range of IPs.
public sealed class IPAddressTools
{
public static UInt32 ConvertIPv4AddressToUInt32(IPAddress address)
{
if (address == null) throw new ArgumentNullException("address", "The value of address is a null reference.");
if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified address's family is invalid.", "address");
Byte[] addressBytes = address.GetAddressBytes();
UInt32 addressInteger = (((UInt32)addressBytes[0]) << 24) + (((UInt32)addressBytes[1]) << 16) + (((UInt32)addressBytes[2]) << 8) + ((UInt32)addressBytes[3]);
return addressInteger;
}
public static IPAddress ConvertUInt32ToIPv4Address(UInt32 addressInteger)
{
if (addressInteger < 0 || addressInteger > 4294967295) throw new ArgumentOutOfRangeException("addressInteger", "The value of addressInteger must be between 0 and 4294967295.");
Byte[] addressBytes = new Byte[4];
addressBytes[0] = (Byte)((addressInteger >> 24) & 0xFF);
addressBytes[1] = (Byte)((addressInteger >> 16) & 0xFF);
addressBytes[2] = (Byte)((addressInteger >> 8) & 0xFF);
addressBytes[3] = (Byte)(addressInteger & 0xFF);
return new IPAddress(addressBytes);
}
public static IPAddress IncrementIPAddress(IPAddress address, int offset)
{
return ModIPAddress(address, 1);
}
public static IPAddress ModIPAddress(IPAddress address, int offset)
{
if (address == null) throw new ArgumentNullException("address", "The value of address is a null reference.");
if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified address's family is invalid.");
UInt32 addressInteger = ConvertIPv4AddressToUInt32(address);
addressInteger += offset;
return ConvertUInt32ToIPv4Address(addressInteger);
}
public static IPAddress[] GetIpRange(IPAddress address, IPAddress mask)
{
if (address == null) throw new ArgumentNullException("address", "The value of address is a null reference.");
if (mask == null) throw new ArgumentNullException("mask", "The value of mask is a null reference.");
if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified address's family is invalid.");
if (mask.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified mask's family is invalid.");
byte[] addressBytes = address.GetAddressBytes();
byte[] maskBytes = mask.GetAddressBytes();
byte[] startIpBytes = new byte[addressBytes.Length];
byte[] endIpBytes = new byte[addressBytes.Length];
for (int i = 0; i < addressBytes.Length; i++)
{
startIpBytes[i] = (byte)(addressBytes[i] & maskBytes[i]);
endIpBytes[i] = (byte)(addressBytes[i] | ~maskBytes[i]);
}
IPAddress startIp = new IPAddress(startIpBytes);
IPAddress endIp = new IPAddress(endIpBytes);
List<IPAddress> addresses = new List<IPAddress>();
for (IPAddress currentIp = startIp; ConvertIPv4AddressToUInt32(currentIp) <= ConvertIPv4AddressToUInt32(endIp); currentIp = IncrementIPAddress(currentIp))
{
addresses.Add(currentIp);
}
return addresses.ToArray();
}
}
You could also implement the + and - operators for the IPAddress class, but since it wouldn't work for all uses of the class it's probably not a good idea.
public static IPAddress operator +(IPAddress address, int offset)
{
if (address == null) throw new ArgumentNullException("address", "The value of address is a null reference.");
if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified address's family is invalid.", "address");
Byte[] addressBytes = address.GetAddressBytes();
UInt32 addressInteger = (((UInt32)addressBytes[0]) << 24) + (((UInt32)addressBytes[1]) << 16) + (((UInt32)addressBytes[2]) << 8) + ((UInt32)addressBytes[3]);
addressInteger += offset;
addressBytes[0] = (Byte)((addressInteger >> 24) & 0xFF);
addressBytes[1] = (Byte)((addressInteger >> 16) & 0xFF);
addressBytes[2] = (Byte)((addressInteger >> 8) & 0xFF);
addressBytes[3] = (Byte)(addressInteger & 0xFF);
return new IPAddress(addressBytes);
}
public static IPAddress operator -(IPAddress address, int offset)
{
if (address == null) throw new ArgumentNullException("address", "The value of address is a null reference.");
if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) throw new ArgumentException("The specified address's family is invalid.", "address");
Byte[] addressBytes = address.GetAddressBytes();
UInt32 addressInteger = (((UInt32)addressBytes[0]) << 24) + (((UInt32)addressBytes[1]) << 16) + (((UInt32)addressBytes[2]) << 8) + ((UInt32)addressBytes[3]);
addressInteger -= offset;
addressBytes[0] = (Byte)((addressInteger >> 24) & 0xFF);
addressBytes[1] = (Byte)((addressInteger >> 16) & 0xFF);
addressBytes[2] = (Byte)((addressInteger >> 8) & 0xFF);
addressBytes[3] = (Byte)(addressInteger & 0xFF);
return new IPAddress(addressBytes);
}
In your cycle you are doing some wild increments. First increment t is 0 so ip stays the same. Second increment t is 3 so 192.168.1.1 becomes 192.168.1.4(and you save it as current). Third increment t is 6 so 192.168.1.4 becomes 192.168.1.10(and saved as current) ...
I think what you are trying to achieve is somthing like this:
string from = "192.168.1.1", to = "192.168.1.255";
uint first = from.ToUInt(), last = to.ToUInt();
ulong total = last - first;
uint offset = 3; //This is an example number, it actually could be anything.
for (uint t = 0; (ulong)t < total; t += 1)
{
uint ut = Convert.ToUInt32(t);
uint c = first + t + offset;
var ip = c.ToIPAddress();
}
Ip can be decomposed as follow
integer value= (4thOctat*2^24.3rd*2^16.2nd*2^8.1st*2^0)
e.g. 64.233.187.99
64*2^24 + 233*2^16 + 187*2^8 + 99
= 1089059683
I wrote this small example for you,
//This is regular expression to check the the ip is in correct format
private readonly Regex ip = new Regex(#"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
private void Main()
{
string IpAddress = "172.22.1.1";
if (ip.IsMatch(IpAddress))
{
//increment first octat by 5
string IncrementedIp = IncrementIP(0, 100, IPAddress.Parse(IpAddress));
if (ip.IsMatch(IncrementedIp))
{
Console.WriteLine("Incremented Ip = {0}", IncrementedIp);
}
else
{
//not valid ip address}
}
}else
{
//Not Valid Ip Address
}
}
private string IncrementIP(short octat, long Offset,IPAddress adress)
{
//octat range from 0-3
if ( octat<0 ||octat > 3) return adress.ToString();
long IpLong = AdressToInt(adress.ToString());
IpLong += (long)(Offset*(Math.Pow(2,octat*8)));
return longToAddress(IpLong);
}
static long AdressToInt(string addr)
{
return (long)(uint)IPAddress.NetworkToHostOrder(
(int)IPAddress.Parse(addr).Address);
}
static string longToAddress(long address)
{
return IPAddress.Parse(address.ToString()).ToString();
}
To evaluate a little: You simply change the numeric representation of a value from one base to another, the another in this case being the 10-base, which your code, your head and your computer language use commonly. In 10-base, you can easily perform arithmetics. Once you have done that, you change the resulting number back to another base again, for example the original one.
In the case of an IP-address, the base is 256. As said earlier, an IP-address is simply a numeric value consisting 32 bits.
Bits are 2-base (toolset: 0,1)
Your calculation happens in 10-base (0,1,2,3,4,5,6,7,8,9)
Hexa is 16-base ((0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)
An IP-address is (4 of) 256-base (0,1,2,3,4,5,6,7,8,9,[we should have another 246 unique symbols here]). As we do not have 256 unique numeric symbols (would be too many anyway), we describe these for convenience using 10-base instead, for example 253 (but 253 should really be the 254th symbol in a symbol table, like in the ASCII-table).
There are a million cases when you want to change base, or change numeric space. One example is incrementing a date. You change to the manageable days-since-20th-centurystart-space (the actual change isn't too simple, but the good result is a 10-base representation), perform the calculation (eg. increment with 7 days), and then change back to YMD-space.
The IP-address 255.255.255.255 could also be described using the 10-base integer value 4294967295.
They Answers are right... for addition to your implementation
CheckAgain:
If My.Computer.Network.Ping(CheckIPFirst) = False Then
'=====IF IP ADDRESS NOT OCCUPIED GET=========
CheckIPFirst +=1
GETIpAddress(Counter) = CheckIPFirst
Else
'======CHECK ANOTHER IP ADDRESS=============
CheckIPFirst +=1
Goto CheckAgain
End If
Through that you will not encounter a IP Address Conflict or Same IP Address.
Interestingly, I can find implementations for the Internet Checksum in almost every language except C#. Does anyone have an implementation to share?
Remember, the internet protocol specifies that:
"The checksum field is the 16 bit one's complement of the one's
complement sum of all 16 bit words in the header. For purposes of
computing the checksum, the value of the checksum field is zero."
More explanation can be found from Dr. Math.
There are some efficiency pointers available, but that's not really a large concern for me at this point.
Please include your tests! (Edit: Valid comment regarding testing someone else's code - but I am going off of the protocol and don't have test vectors of my own and would rather unit test it than put into production to see if it matches what is currently being used! ;-)
Edit: Here are some unit tests that I came up with. They test an extension method which iterates through the entire byte collection. Please comment if you find fault in the tests.
[TestMethod()]
public void InternetChecksum_SimplestValidValue_ShouldMatch()
{
IEnumerable<byte> value = new byte[1]; // should work for any-length array of zeros
ushort expected = 0xFFFF;
ushort actual = value.InternetChecksum();
Assert.AreEqual(expected, actual);
}
[TestMethod()]
public void InternetChecksum_ValidSingleByteExtreme_ShouldMatch()
{
IEnumerable<byte> value = new byte[]{0xFF};
ushort expected = 0xFF;
ushort actual = value.InternetChecksum();
Assert.AreEqual(expected, actual);
}
[TestMethod()]
public void InternetChecksum_ValidMultiByteExtrema_ShouldMatch()
{
IEnumerable<byte> value = new byte[] { 0x00, 0xFF };
ushort expected = 0xFF00;
ushort actual = value.InternetChecksum();
Assert.AreEqual(expected, actual);
}
I knew I had this one stored away somewhere...
http://cyb3rspy.wordpress.com/2008/03/27/ip-header-checksum-function-in-c/
Well, I dug up an implementation from an old code base and it passes the tests I specified in the question, so here it is (as an extension method):
public static ushort InternetChecksum(this IEnumerable<byte> value)
{
byte[] buffer = value.ToArray();
int length = buffer.Length;
int i = 0;
UInt32 sum = 0;
UInt32 data = 0;
while (length > 1)
{
data = 0;
data = (UInt32)(
((UInt32)(buffer[i]) << 8)
|
((UInt32)(buffer[i + 1]) & 0xFF)
);
sum += data;
if ((sum & 0xFFFF0000) > 0)
{
sum = sum & 0xFFFF;
sum += 1;
}
i += 2;
length -= 2;
}
if (length > 0)
{
sum += (UInt32)(buffer[i] << 8);
//sum += (UInt32)(buffer[i]);
if ((sum & 0xFFFF0000) > 0)
{
sum = sum & 0xFFFF;
sum += 1;
}
}
sum = ~sum;
sum = sum & 0xFFFF;
return (UInt16)sum;
}
I have made an implementation of the IPv4 header checksum calculation, as defined in RFC 791.
Extension Methods
public static ushort GetInternetChecksum(this ReadOnlySpan<byte> bytes)
=> CalculateChecksum(bytes, ignoreHeaderChecksum: true);
public static bool IsValidChecksum(this ReadOnlySpan<byte> bytes)
// Should equal zero (valid)
=> CalculateChecksum(bytes, ignoreHeaderChecksum: false) == 0;
The Checksum Calculation
using System.Buffers.Binary;
private static ushort CalculateChecksum(ReadOnlySpan<byte> bytes, bool ignoreHeaderChecksum)
{
ushort checksum = 0;
for (int i = 0; i <= 18; i += 2)
{
// i = 0 e.g. [0..2] Version and Internal Header Length
// i = 2 e.g. [2..4] Total Length
// i = 4 e.g. [4..6] Identification
// i = 6 e.g. [6..8] Flags and Fragmentation Offset
// i = 8 e.g. [8..10] TTL and Protocol
// i = 10 e.g. [10..12] Header Checksum
// i = 12 e.g. [12..14] Source Address #1
// i = 14 e.g. [14..16] Source Address #2
// i = 16 e.g. [16..18] Destination Address #1
// i = 18 e.g. [18..20] Destination Address #2
if (ignoreHeaderChecksum && i == 10) continue;
ushort value = BinaryPrimitives.ReadUInt16BigEndian(bytes[i..(i + 2)]);
// Each time a carry occurs, we must add a 1 to the sum
if (checksum + value > ushort.MaxValue)
{
checksum++;
}
checksum += value;
}
// One’s complement
return (ushort)~checksum;
}