SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

Socketを利用したパケットモニタ (C#)(F#)


IP層で動作するパケットモニタです。
SocketType.Raw, ProtocolType.IPでSocketを作成し、IOControlでIOControlCode.ReceiveAllをセットします。
IOControlCode.ReceiveAllは、SIO_RCVALLと同じです。

取得したデータは、IP層のデータですので、8バイト目がTTL、9バイト目がプロトコル番号、12バイト目から4バイトが送信IPアドレス、16バイト目から4バイトが受信IPアドレスとして解釈して表示しています。
IP層で動作していますので、当然ですがMACアドレスやIP以外のパケット(ARPなど)を表示する事はできません。

C#サンプル

using System;
using System.Net;
using System.Net.Sockets;
using System.Linq;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            //インターフェースのアドレス取得            
            var he = Dns.GetHostEntry(Dns.GetHostName());
            var addr = he.AddressList.Where((h) => h.AddressFamily == AddressFamily.InterNetwork).ToList();
            
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
            socket.Bind(new IPEndPoint(addr[0], 0));
            socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.AcceptConnection,1);
            byte [] ib = new byte[]{1,0,0,0};
            byte [] ob = new byte[]{0,0,0,0};
            socket.IOControl(IOControlCode.ReceiveAll, ib, ob);//SIO_RCVALL
            byte[] buf = new byte[4096];
            int i = 0;
            while (true) {
                IAsyncResult iares = socket.BeginReceive(buf, 0, buf.Length, SocketFlags.None, null, null);
                var len = socket.EndReceive(iares);
                Console.WriteLine("[{0}] Protocol={1} src={2} dst={3} TTL={4} Len={5}"
                                      ,i++,Proto(buf[9]),Ip(buf,12),Ip(buf,16),buf[8],len);
            }
        }
        static string Ip(byte [] buf,int i){
            return string.Format("{0}.{1}.{2}.{3}", buf[i], buf[i+1], buf[i+2], buf[i+3]);
        }
        static string Proto(byte b) {
            if(b==6)
                return "TCP";
            else if(b==17)
                return "UDP";
           return "Other";
        }
    }
}

F#サンプル

open System
open System.Net
open System.Net.Sockets
open System.Linq

let Ip(buf:byte[],i)=
    sprintf "%d.%d.%d.%d" (buf.[i]) (buf.[i+1]) (buf.[i+2]) (buf.[i+3])

let Proto(b)=
    match b with
    |6uy -> "TCP"
    |17uy -> "UDP"
    |_ -> "other"

//インターフェースのアドレス取得
let addr:IPAddress = 
    Dns.GetHostEntry(Dns.GetHostName())
    |>fun n -> n.AddressList
    |>Seq.filter(fun h -> h.AddressFamily = AddressFamily.InterNetwork)
    |>fun n -> n.First()
    
let socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP)
socket.Bind(new IPEndPoint(addr, 0))
socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.AcceptConnection,1)

let ib = [|1uy;0uy;0uy;0uy|]
let ob = [|0uy;0uy;0uy;0uy|]
socket.IOControl(IOControlCode.ReceiveAll,ib, ob)|>ignore//SIO_RCVALL

let buf : byte array = Array.create 4096 0uy
let mutable i=0
while true do
    let iares = socket.BeginReceive(buf, 0, buf.Length, SocketFlags.None, null, null)
    let len = socket.EndReceive(iares)
    printfn "[%d] Protocol=%s src=%s dst=%s TTL=%d Len=%d"
                     i (Proto(buf.[9])) (Ip(buf,12)) (Ip(buf,16)) (buf.[8]) len
    i <- i+1