Sockets Haskell中的原始套接字

Sockets Haskell中的原始套接字,sockets,unix,networking,haskell,Sockets,Unix,Networking,Haskell,Network.socket中提供了原始套接字类型,但在绑定套接字附近有一条注释“当前仅支持Unix域套接字和Internet系列”。如何在haskell中使用原始套接字 我试图实现的,作为Python代码: import binascii import socket # Create raw socket. ethType = b'FFFF' # 2 bytes, has to be >= 0x0600. FFFF is "unavailable" by IEEE. sock = so

Network.socket中提供了原始套接字类型,但在绑定套接字附近有一条注释“当前仅支持Unix域套接字和Internet系列”。如何在haskell中使用原始套接字

我试图实现的,作为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这个接口受支持。

首先,您不需要绑定原始套接字就可以使用它。来自
man 7 raw

可以使用bind(2)调用将原始套接字绑定到特定的本地地址。如果未绑定,则接收具有指定IP协议的所有数据包

如果要绑定它,则可以使用
bindSocket
,因为原始套接字使用与
ip
使用相同的
sockaddr
结构:

原始套接字在ip(7)中定义的地址结构中使用标准sockaddr_

可用于发送和接收原始数据

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))
将限定数据.ByteString.Char8作为B导入
导入网络.Pcap
main=do
--开放式设备
开发计算机(显示数据包))

也有类似的任务,我唯一能做的就是使用。当前的
网络
(撰写本文时为2.4.0.1)只知道三个系列,其中没有一个适合
AF\u数据包
套接字。需要的是
struct sockaddr\u ll
,它不可用

有一次,虽然它被合并了,但似乎最终还是丢失了

我的代码是脏的(对不起,这是我第一个“认真”的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 ()

……你的问题是什么?`得到了支持。在我看来,这是一个直截了当的翻译。我已经成功地创建了一个套接字,但是要将它绑定到什么呢?怎么做?我建议你把你写的Haskell代码添加到问题中,否则你将很难得到具体的答案。还要更好地指出翻译中存在的问题。使用。为什么这样我就可以在不绑定套接字的情况下接收,但无法发送(使用上面的Python代码进行了尝试,得到了“[Errno 6]没有这样的设备或地址”)?@Andres错误消息听起来像是发送到了无效位置。要发送数据,操作系统至少需要决定应该使用什么接口(例如
lo
eth0
)。因此,您需要使用
So\u BINDTODEVICE
socket选项绑定到地址或绑定到设备。AFAIK
网络不支持最后一个package@Yuras就我所见,如果我将sockaddr_用于IP,我可以将它们与原始套接字一起使用,但在其他方面,它似乎毫无意义。“struct sockaddr_in是“Beej的网络编程指南”中的“IPv4地址使用的结构”。我怀疑发送到无效位置是问题所在,因为绑定套接字后,我可以从mac 0发送到mac 0,但它仍然会被传输。@Yuras from man 7 socket,SO_BINDTODEVICE:“请注意,这只适用于某些套接字类型,特别是AF_INET套接字。数据包套接字不支持它(在那里使用普通绑定(2)。”,事实并非如此。我相信
Network.Socket
不知道绑定
AF\u数据包所需的任何
struct sockaddr\u ll
。仅支持Inet、Inet 6和Unix风格的地址。
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)
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))
{-# 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 ()
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