You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中构建RARP数据包并响应服务器RARP请求?

在C#中构建RARP响应数据包的完整方案

我明白你现在的困境——RARP确实是个几乎被遗忘的协议,相关资料少得可怜,不过别担心,我可以给你梳理清楚在C#里构建RARP响应包的核心步骤和代码实现。

首先得明确RARP数据包的底层结构,这是构建响应的核心:RARP封装在以太网帧中,以太帧类型为0x8035,而RARP包本身的固定结构如下:

  • 硬件类型(2字节,以太网对应0x0001)
  • 协议类型(2字节,IPv4对应0x0800)
  • 硬件地址长度(1字节,MAC地址是6)
  • 协议地址长度(1字节,IPv4地址是4)
  • 操作码(2字节,1表示请求,2表示响应)
  • 发送端硬件地址(6字节)
  • 发送端协议地址(4字节)
  • 目标硬件地址(6字节)
  • 目标协议地址(4字节)

接下来我们用原生C# Socket来实现监听请求和发送响应的逻辑,步骤如下:

1. 定义数据包结构体

用结构体来映射以太网帧头和RARP包,注意设置Pack=1避免字节对齐问题:

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct EthernetHeader
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] DestinationMac;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] SourceMac;
    public ushort EtherType;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RarpPacket
{
    public ushort HardwareType;
    public ushort ProtocolType;
    public byte HardwareAddrLength;
    public byte ProtocolAddrLength;
    public ushort OpCode;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] SenderHardwareAddr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] SenderProtocolAddr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] TargetHardwareAddr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] TargetProtocolAddr;
}

2. 结构体与字节数组互转工具

因为Socket操作的是字节流,需要把结构体和字节数组互相转换:

public static byte[] StructToBytes(object obj)
{
    int size = Marshal.SizeOf(obj);
    byte[] buffer = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(obj, ptr, true);
    Marshal.Copy(ptr, buffer, 0, size);
    Marshal.FreeHGlobal(ptr);
    return buffer;
}

public static T BytesToStruct<T>(byte[] buffer) where T : struct
{
    int size = Marshal.SizeOf(typeof(T));
    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.Copy(buffer, 0, ptr, size);
    T obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
    Marshal.FreeHGlobal(ptr);
    return obj;
}

3. 监听RARP请求并发送响应

核心逻辑是用原始Socket捕获以太网帧,过滤RARP请求,然后构造响应包发送:

public static void StartRarpResponder()
{
    // 必须以管理员权限运行!
    using (Socket rawSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Raw))
    {
        // 绑定到服务器的网卡IP,替换成你的实际IP
        IPAddress localIp = IPAddress.Parse("192.168.1.10");
        rawSocket.Bind(new IPEndPoint(localIp, 0));
        
        // 配置Socket以接收完整的以太网帧头部
        rawSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);

        byte[] receiveBuffer = new byte[4096];
        Console.WriteLine("Listening for RARP requests...");

        while (true)
        {
            int bytesReceived = rawSocket.Receive(receiveBuffer);
            if (bytesReceived < Marshal.SizeOf<EthernetHeader>() + Marshal.SizeOf<RarpPacket>())
                continue;

            // 解析以太网帧头
            EthernetHeader ethHeader = BytesToStruct<EthernetHeader>(receiveBuffer);
            ushort etherType = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(ethHeader.EtherType, 0));
            
            // 过滤非RARP帧
            if (etherType != 0x8035)
                continue;

            // 解析RARP请求包
            int rarpOffset = Marshal.SizeOf<EthernetHeader>();
            RarpPacket rarpRequest = BytesToStruct<RarpPacket>(receiveBuffer.AsSpan(rarpOffset).ToArray());
            ushort opCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(rarpRequest.OpCode, 0));
            
            // 只处理RARP请求(操作码=1)
            if (opCode != 1)
                continue;

            // 检查请求的目标MAC是否为全0(根据你的需求)
            bool isTargetMacAllZero = Array.TrueForAll(rarpRequest.TargetHardwareAddr, b => b == 0);
            if (!isTargetMacAllZero)
                continue;

            // 替换成你的服务器MAC地址
            byte[] serverMac = new byte[] { 0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E };
            // 根据你的解析方法获取要分配的IP地址
            IPAddress assignedIp = IPAddress.Parse("192.168.1.100");

            // 构造RARP响应包
            RarpPacket rarpReply = new RarpPacket
            {
                HardwareType = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)0x0001)),
                ProtocolType = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)0x0800)),
                HardwareAddrLength = 6,
                ProtocolAddrLength = 4,
                OpCode = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)2)), // 响应操作码
                SenderHardwareAddr = serverMac,
                SenderProtocolAddr = localIp.GetAddressBytes(),
                TargetHardwareAddr = rarpRequest.SenderHardwareAddr, // 请求端的MAC
                TargetProtocolAddr = assignedIp.GetAddressBytes()
            };

            // 构造响应的以太网帧头
            EthernetHeader replyEthHeader = new EthernetHeader
            {
                DestinationMac = rarpRequest.SenderHardwareAddr,
                SourceMac = serverMac,
                EtherType = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)0x8035))
            };

            // 拼接帧头和RARP包
            byte[] ethBytes = StructToBytes(replyEthHeader);
            byte[] rarpBytes = StructToBytes(rarpReply);
            byte[] replyPacket = new byte[ethBytes.Length + rarpBytes.Length];
            Buffer.BlockCopy(ethBytes, 0, replyPacket, 0, ethBytes.Length);
            Buffer.BlockCopy(rarpBytes, 0, replyPacket, ethBytes.Length, rarpBytes.Length);

            // 发送响应
            rawSocket.Send(replyPacket);
            Console.WriteLine($"Sent RARP reply to MAC {BitConverter.ToString(rarpRequest.SenderHardwareAddr).Replace("-", ":")}, assigned IP: {assignedIp}");
        }
    }
}

关键注意事项

  • 权限要求:程序必须以管理员身份运行,因为原始Socket需要系统级权限。
  • 字节序处理:RARP所有多字节字段都使用网络字节序(大端),所以一定要用IPAddress.HostToNetworkOrder和IPAddress.NetworkToHostOrder进行转换。
  • MAC地址获取:你可以通过System.Net.NetworkInformation.NetworkInterface类自动获取服务器网卡的MAC,避免手动硬编码。
  • 替代方案:如果手动拼接字节太繁琐,也可以使用PacketDotNet库(它内置了RARP数据包的解析和构建逻辑),不过原生实现更可控,适合你这种硬性要求的场景。

内容的提问来源于stack exchange,提问作者kdun0508

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:23:27