5

Network.Socketで提供されるrawソケットタイプがありますが、バインディングソケットの近くに、「現在、Unixドメインソケットとインターネットファミリのみがサポートされています」というコメントがあります。Haskellでrawソケットを使用するにはどうすればよいですか?

動作するPythonコードとして、私が達成しようとしていること:

import binascii
import socket

# Create raw socket.
ethType = b'FFFF' # 2 bytes, has to be >= 0x0600. FFFF is "unavailable" by IEEE.
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
sock.bind( ('lo', int(ethType,16)) )

# Create packet.
srcMac  = b'000000000000' # 6 bytes
destMac = b'000000000000' # 6 bytes
header = binascii.a2b_hex( destMac + srcMac + ethType ) # 14 bytes
message = b'Hello, World!'
sock.send(header + message)

# Receive such packets
while True: print (sock.recv(65535))

編集1:Haskellではsock <- socket AF_PACKET Raw 0xFFFF、ソケットを作成するために使用しますが、引数としてbindSocketaが必要であり、使用可能なコンストラクターは次のとおりです。SockAddr

SockAddrInet PortNumber HostAddress  
SockAddrInet6 PortNumber FlowInfo HostAddress6 ScopeID   
SockAddrUnix String

しかし、これらのどれも正しくないようです。

EDIT2:Yurasのコメントのおかげで、パケットを受信して​​動作するようになりました。

import Network.Socket
sock <- socket AF_PACKET Raw 0xFFFF
recv sock 0xFFFF

EDIT3:バインドせずにソケットからパケットを送信しようとすると、例外が発生します。

sock <- socket AF_PACKET Raw 0xFFFF
send sock "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\255\255"
*** Exception: send: does not exist (No such device or address)

カーネルには、実際にパケットを送信するインターフェイスの手がかりがないため、これは理にかなっています。(raw)ソケットをHaskellのインターフェースにバインドする方法はありますか?

4

4 に答える 4

4

Network.Pcapを使用して、生データを送受信できます。

import qualified Data.ByteString.Char8 as B
import Network.Pcap

main = do
    -- open device
    dev <- openLive "lo" 65535 False 0
    setFilter dev "ether proto 0xFFFF" True 0

    -- send
    sendPacketBS dev (B.pack "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\255\255Hello, World!")

    -- receive
    loopBS dev (-1) (\_ packet -> putStrLn (show packet))
于 2012-04-21T11:38:58.270 に答える
1

このインターフェースはNetwork.Socketでサポートされています。

于 2012-04-19T13:50:16.137 に答える
1

まず第一に、それを使用するためにrawソケットをバインドする必要はありません。からman 7 raw

bind(2)呼び出しを使用して、rawソケットを特定のローカルアドレスにバインドできます。バインドされていない場合は、指定されたIPプロトコルを持つすべてのパケットが受信されます。

バインドする場合はbindSocket、rawソケットがusesと同じsockaddr構造を使用するため、を使用できます。ip

rawソケットは、ip(7)で定義されている標準のsockaddr_inアドレス構造を使用します。

于 2012-04-19T15:06:54.720 に答える
1

同様のタスクがあり、私が得ることができる唯一の方法はFFIを使用することです。Current network(執筆時点では2.4.0.1)は、3つのファミリについてのみ知っており、いずれもソケットSockAddrには適していません。AF_PACKET必要なのはstruct sockaddr_ll、利用できないです。

追加されたパッチSockAddrRawがありましたが、マージされたものの、やがて失われたようです。

私のコードは汚れています(申し訳ありませんが、これは私の最初の「深刻な」FFIコードです)が、とにかくそれを共有したいと思いました。

まず、を作成しましBindPacketHack.hscた。

{-# LANGUAGE CPP, ForeignFunctionInterface #-}

module BindPacketHack (bindPacket) where

import Foreign
import Foreign.C.Types
import Foreign.C.String
import Network.Socket
import Network.Socket.Internal (zeroMemory)

#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if.h>

data SockAddrLL = SockAddrLL { family :: CShort
                             , protocol :: CShort
                             , ifindex :: CInt }
   deriving (Eq, Show)

instance Storable SockAddrLL where
    sizeOf _    = (#size struct sockaddr_ll)
    alignment _ = alignment (undefined :: CInt)
    peek _      = error "peek is not implemented, sorry"
    poke ptr sa = do
        zeroMemory ptr (fromIntegral $ sizeOf sa)
        (#poke struct sockaddr_ll, sll_family) ptr (family sa)
        (#poke struct sockaddr_ll, sll_protocol) ptr (protocol sa)
        (#poke struct sockaddr_ll, sll_ifindex) ptr (ifindex sa)


foreign import ccall unsafe "if_nametoindex"
    c_if_nametoindex :: CString -> IO CInt

ifaceIndex :: String -> IO CInt
ifaceIndex name = withCString name c_if_nametoindex


foreign import ccall unsafe "bind"
    c_bind_ll :: CInt -> Ptr SockAddrLL -> CInt -> IO CInt

bindLL :: Socket -> SockAddrLL -> IO Integer
bindLL s sa = alloca $ \ptr -> do
    poke ptr sa
    ret <- c_bind_ll (fdSocket s) ptr (fromIntegral $ sizeOf sa)
    return $ toInteger ret


bindPacket :: Socket -> ProtocolNumber -> String -> IO ()
bindPacket s proto ifname = do
    ifindex <- ifaceIndex ifname
    bindLL s (sockAddrLL (fromIntegral proto) ifindex)
    return ()

お気づきかもしれませんが、SockAddrLLは不完全であるため、私の実装はかなり制限されています。これは、これら3つのフィールドのみが必要であり、残りはゼロになっているためです。ただし、さらに追加するのは簡単です。

を実行hsc2hs BindPacketHack.hscすると、Cヘッダーファイルが解析され、.hsファイルが表示されます。これで、次のように使用できます。

s <- socket AF_PACKET Raw eth_PPPoEDiscovery
setFdOption (Fd $ fdSocket s) CloseOnExec True
setSocketOption s Broadcast 1
bindPacket s eth_PPPoEDiscovery "eth0"
send s rawPPPoEPacket -- don't forget Ethernet header
于 2012-12-18T12:36:39.593 に答える