首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Internet协议版本4数据报-校验和计算器

Internet协议版本4数据报-校验和计算器
EN

Code Review用户
提问于 2022-07-02 09:02:06
回答 2查看 149关注 0票数 2

我找不到IPv4头校验和计算的实现。所以,我决定按照RFC 791中的定义来做一个。

我使用了“更新的”ReadOnlySpan类来对事物进行一些优化。感谢你的反馈!

扩展方法

注意:这是两种扩展方法,用于说明在读取IPv4数据包中包含的字节的上下文中只需要计算校验和的两个用例。它们生成API来公开私有方法(如下所示)。它们只是通过忽略第10字节(头校验和)来区别。

代码语言:javascript
复制
public static ushort GetInternetChecksum(this ReadOnlySpan bytes)
    => CalculateChecksum(bytes, ignoreHeaderChecksum: true);
代码语言:javascript
复制
public static bool IsValidChecksum(this ReadOnlySpan bytes)
    // Should equal zero (valid)
    => CalculateChecksum(bytes, ignoreHeaderChecksum: false) == 0;

校验和计算

代码语言:javascript
复制
using System.Buffers.Binary;

/// 
/// An implementation of the IPv4 header checksum calculation, 
/// as defined in RFC 791.
/// 
private static ushort CalculateChecksum(
    ReadOnlySpan bytes, 
    bool ignoreHeaderChecksum)
{
    ushort checksum = 0;
    for (int i = 0; i <= 18; i += 2)
    {
        // Ignore the Header Checksum?
        if (ignoreHeaderChecksum && i == 10) continue;

        // Use network byte order
        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;
}

关于上述循环的详细说明:

代码语言:javascript
复制
i = 0   e.g. bytes[0..2]   // Version and Internal Header Length
i = 2   e.g. bytes[2..4]   // Total Length
i = 4   e.g. bytes[4..6]   // Identification
i = 6   e.g. bytes[6..8]   // Flags and Fragmentation Offset
i = 8   e.g. bytes[8..10]  // TTL and Protocol
i = 10  e.g. bytes[10..12] // ***Header Checksum***
i = 12  e.g. bytes[12..14] // Source Address #1
i = 14  e.g. bytes[14..16] // Source Address #2
i = 16  e.g. bytes[16..18] // Destination Address #1
i = 18  e.g. bytes[18..20] // Destination Address #2

// Note: We should ignore the Header Checksum value
//       when calculating the checksum, and include
//       it back in when verifying.

<#>EDIT:我在扩展方法部分(上面)添加了一些额外的上下文,我还添加了一个用例(下面)。

示例用法

CapturePacketsAsync()片段

代码语言:javascript
复制
PipeOptions options = new(
   pauseWriterThreshold: 65535, // 64kB
   resumeWriterThreshold: 1400); // or MTU - TCP/IP Headers
Pipe pipe = new(options);
Task writing = WriteToPipeAsync(pipe.Writer, _socket, cancellationToken);
Task reading = ReadFromPipeAsync(pipe.Reader, cancellationToken);
await Task.WhenAll(reading, writing).ConfigureAwait(true);

WriteToPipeAsync()片段

代码语言:javascript
复制
while(!cancellationToken.IsCancellationRequested)
{
   const int minimumBufferSize = 65535;
   Memory buffer = writer.GetMemory(minimumBufferSize);
   _ = await socket.ReceiveAsync(buffer, socketFlags, cancellationToken);

   // We can perform either of the checksum methods here
   //    - use GetInternetChecksum() if we are sending
   //    - use IsValidChecksum() if we are receiving
   // Then handle any invalid packets

   writer.Advance(bytesReceived);

   // try/finally, mark pipe complete via CompleteAsync(), etc.
}

ReadFromPipeAsync()片段

代码语言:javascript
复制
while(!cancellationToken.IsCancellationRequested)
{
   ReadResult result = await reader.ReadAsync(cancellationToken).ConfigureAwait(true);
   ReadOnlySequence buffer = result.Buffer;

   // We can perform either of the checksum methods here
   //    - use GetInternetChecksum() if we are sending
   //    - use IsValidChecksum() if we are receiving
   // Then handle any invalid packets

   reader.AdvanceTo(consumed, examined);

   // try/finally, mark pipe complete via CompleteAsync(), etc.
}

在上述情况下,进行校验和计算的选择将取决于您希望如何管理管道两侧的压力。

EN

回答 2

Code Review用户

发布于 2022-07-02 16:13:18

这是一段有趣的代码,我很高兴它适用于您的特定用法,但在一般情况下,我发现它缺乏。

首先,您有零检查来验证输入的bytes表单(一个有效的IPv4报头)。一个人所要做的就是传递一个任意长度的ReadOnlySpan,包括0长度。

我还发现它有一定的限制性,因为您将输入限制为ReadOnlySpan。如果某人有一个数组或一个列表,他们就倒霉了。我倾向于这样一种理念,即公共投入参数应尽可能广泛地提供。这意味着IEnumerable的投入将具有最广泛的吸引力。但是,无论输入是ReadOnlySpanbyte[]List还是任何可枚举的byte集合,都需要对输入进行验证,以确定它是否形成了合适的D7头。

虽然主要的计算是大量的数学运算,并且应该是快速的,但是我发现关于IsValidChecksum的一些效率很低。我想弄清楚你会怎么用它。以下是两种可能的方法:

Branch 1:

代码语言:javascript
复制
var checksum = GetInternetChecksum(bytes);
if (IsValidChecksum(bytes))  // <== calculated twice
{
   // do something
}

Branch 2:

代码语言:javascript
复制
if (IsValidChecksum(bytes))  
{
    var checksum = GetInternetChecksum(bytes);  // <== calculated twice
   // do something
}

你看到的是,在这2种可能的用法中,你必须执行两次计算,这只是看起来不对。

票数 3
EN

Code Review用户

发布于 2022-07-03 09:06:19

除了“里克-达文”,

  • 太宽泛的方法名称:GetInternetChecksumIsValidChecksumCalculateChecksum,它们不知道哪个type of data将验证或计算它的校验和。
  • 针对一个非常普通的类型而不是特定类型。

如果您只让它们是私有的,并且只为IPAddress提供了在内部使用它们的公共扩展方法,那么它可能会更好,因为将范围缩小到特定类型,并且还可以节省验证IP的工作和时间,您可能只需要根据特定的IP方案进行验证,如果需要的话,它还将打开许多其他受.NET支持的方案。

而且,由于字节大小和位置是固定的,所以可能使用一个以可读形式表示地址的类将使使用起来更加容易,并且只需要验证一次。

就像这样:

代码语言:javascript
复制
public class IPAddressChecksumValidator
{
    private readonly IPAddressEntry _entry; 
    
    private class IPAddressEntry
    {
        public byte Version { get; set; }
        public byte TotalLength { get; set; }
        public byte Identification { get; set; }
        // ..etc.
        public IPAddressEntry(byte[] entry)
        {
            /// assign bytes to each property 
        }
    }
    
    public IPAddressChecksumValidator(IPAddress ipAddress)
    {
        // initialize and validate IPAddress version
    }
    
    public ushort GetInternetChecksum() { 
        // use _entry to get the result
    }
    
    public bool IsValid() { 
        // use _entry to get the result     
    }
}


public static class IPAddressExtension
{
    public static IPAddressClass GetValidator(this IPAddress ipAddress)
        => new IPAddressChecksumValidator(ipAddress);
}

用法示例:

代码语言:javascript
复制
var ipAddress = new IPAddress(bytes);

var checksumValidator = ipAddress.GetValidator(); 
// or new IPAddressChecksumValidator(ipAddress);

if(checksumValidator.IsValid())
{
    // do stuff
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/277811

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档